brighterscript 0.41.2 → 1.0.0-alpha.12

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 (170) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +2 -2
  3. package/dist/DiagnosticCollection.js +2 -2
  4. package/dist/DiagnosticCollection.js.map +1 -1
  5. package/dist/DiagnosticFilterer.js +2 -2
  6. package/dist/DiagnosticFilterer.js.map +1 -1
  7. package/dist/DiagnosticMessages.d.ts +6 -1
  8. package/dist/DiagnosticMessages.js +5 -0
  9. package/dist/DiagnosticMessages.js.map +1 -1
  10. package/dist/LanguageServer.d.ts +8 -2
  11. package/dist/LanguageServer.js +50 -44
  12. package/dist/LanguageServer.js.map +1 -1
  13. package/dist/Program.d.ts +61 -45
  14. package/dist/Program.js +305 -188
  15. package/dist/Program.js.map +1 -1
  16. package/dist/ProgramBuilder.d.ts +7 -7
  17. package/dist/ProgramBuilder.js +60 -51
  18. package/dist/ProgramBuilder.js.map +1 -1
  19. package/dist/Scope.d.ts +42 -19
  20. package/dist/Scope.js +261 -129
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/SymbolTable.d.ts +73 -0
  23. package/dist/SymbolTable.js +157 -0
  24. package/dist/SymbolTable.js.map +1 -0
  25. package/dist/XmlScope.d.ts +5 -0
  26. package/dist/XmlScope.js +66 -28
  27. package/dist/XmlScope.js.map +1 -1
  28. package/dist/astUtils/creators.d.ts +15 -1
  29. package/dist/astUtils/creators.js +39 -9
  30. package/dist/astUtils/creators.js.map +1 -1
  31. package/dist/astUtils/reflection.d.ts +28 -16
  32. package/dist/astUtils/reflection.js +52 -30
  33. package/dist/astUtils/reflection.js.map +1 -1
  34. package/dist/astUtils/reflection.spec.js +3 -3
  35. package/dist/astUtils/reflection.spec.js.map +1 -1
  36. package/dist/astUtils/visitors.spec.js +12 -13
  37. package/dist/astUtils/visitors.spec.js.map +1 -1
  38. package/dist/astUtils/xml.d.ts +3 -3
  39. package/dist/astUtils/xml.js +2 -2
  40. package/dist/astUtils/xml.js.map +1 -1
  41. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +5 -6
  42. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  43. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +24 -22
  44. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  45. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js +2 -2
  46. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +1 -1
  47. package/dist/examples/plugins/removePrint.js +1 -1
  48. package/dist/examples/plugins/removePrint.js.map +1 -1
  49. package/dist/files/BrsFile.Class.spec.js +356 -41
  50. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  51. package/dist/files/BrsFile.d.ts +55 -37
  52. package/dist/files/BrsFile.js +430 -399
  53. package/dist/files/BrsFile.js.map +1 -1
  54. package/dist/files/BrsFile.spec.js +199 -158
  55. package/dist/files/BrsFile.spec.js.map +1 -1
  56. package/dist/files/XmlFile.d.ts +20 -9
  57. package/dist/files/XmlFile.js +36 -31
  58. package/dist/files/XmlFile.js.map +1 -1
  59. package/dist/files/XmlFile.spec.js +113 -113
  60. package/dist/files/XmlFile.spec.js.map +1 -1
  61. package/dist/files/tests/imports.spec.js +32 -32
  62. package/dist/files/tests/imports.spec.js.map +1 -1
  63. package/dist/globalCallables.js +17 -6
  64. package/dist/globalCallables.js.map +1 -1
  65. package/dist/interfaces.d.ts +155 -39
  66. package/dist/parser/BrsTranspileState.d.ts +7 -0
  67. package/dist/parser/BrsTranspileState.js +10 -1
  68. package/dist/parser/BrsTranspileState.js.map +1 -1
  69. package/dist/parser/Expression.d.ts +23 -12
  70. package/dist/parser/Expression.js +45 -30
  71. package/dist/parser/Expression.js.map +1 -1
  72. package/dist/parser/Parser.Class.spec.js +100 -1
  73. package/dist/parser/Parser.Class.spec.js.map +1 -1
  74. package/dist/parser/Parser.d.ts +118 -5
  75. package/dist/parser/Parser.js +398 -37
  76. package/dist/parser/Parser.js.map +1 -1
  77. package/dist/parser/Parser.spec.js +404 -7
  78. package/dist/parser/Parser.spec.js.map +1 -1
  79. package/dist/parser/SGParser.d.ts +41 -4
  80. package/dist/parser/SGParser.js +185 -174
  81. package/dist/parser/SGParser.js.map +1 -1
  82. package/dist/parser/SGParser.spec.js +17 -4
  83. package/dist/parser/SGParser.spec.js.map +1 -1
  84. package/dist/parser/SGTypes.d.ts +203 -38
  85. package/dist/parser/SGTypes.js +464 -160
  86. package/dist/parser/SGTypes.js.map +1 -1
  87. package/dist/parser/SGTypes.spec.d.ts +1 -0
  88. package/dist/parser/SGTypes.spec.js +351 -0
  89. package/dist/parser/SGTypes.spec.js.map +1 -0
  90. package/dist/parser/Statement.d.ts +37 -26
  91. package/dist/parser/Statement.js +81 -20
  92. package/dist/parser/Statement.js.map +1 -1
  93. package/dist/parser/Statement.spec.js +5 -5
  94. package/dist/parser/Statement.spec.js.map +1 -1
  95. package/dist/parser/TranspileState.d.ts +1 -1
  96. package/dist/parser/TranspileState.js +15 -7
  97. package/dist/parser/TranspileState.js.map +1 -1
  98. package/dist/parser/tests/controlFlow/ForEach.spec.js +5 -4
  99. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  100. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +1 -1
  101. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  102. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +1 -1
  103. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  104. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +1 -1
  105. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  106. package/dist/parser/tests/expression/TernaryExpression.spec.js +1 -1
  107. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  108. package/dist/types/ArrayType.d.ts +1 -0
  109. package/dist/types/ArrayType.js +23 -19
  110. package/dist/types/ArrayType.js.map +1 -1
  111. package/dist/types/BooleanType.d.ts +3 -2
  112. package/dist/types/BooleanType.js +6 -3
  113. package/dist/types/BooleanType.js.map +1 -1
  114. package/dist/types/BscType.d.ts +19 -4
  115. package/dist/types/BscType.js +9 -0
  116. package/dist/types/BscType.js.map +1 -1
  117. package/dist/types/CustomType.d.ts +8 -5
  118. package/dist/types/CustomType.js +12 -12
  119. package/dist/types/CustomType.js.map +1 -1
  120. package/dist/types/DoubleType.d.ts +1 -0
  121. package/dist/types/DoubleType.js +11 -11
  122. package/dist/types/DoubleType.js.map +1 -1
  123. package/dist/types/DynamicType.d.ts +1 -0
  124. package/dist/types/DynamicType.js +4 -0
  125. package/dist/types/DynamicType.js.map +1 -1
  126. package/dist/types/FloatType.d.ts +2 -1
  127. package/dist/types/FloatType.js +11 -11
  128. package/dist/types/FloatType.js.map +1 -1
  129. package/dist/types/FunctionType.d.ts +13 -10
  130. package/dist/types/FunctionType.js +34 -18
  131. package/dist/types/FunctionType.js.map +1 -1
  132. package/dist/types/FunctionType.spec.js +8 -2
  133. package/dist/types/FunctionType.spec.js.map +1 -1
  134. package/dist/types/IntegerType.d.ts +2 -1
  135. package/dist/types/IntegerType.js +11 -11
  136. package/dist/types/IntegerType.js.map +1 -1
  137. package/dist/types/InvalidType.d.ts +3 -2
  138. package/dist/types/InvalidType.js +7 -4
  139. package/dist/types/InvalidType.js.map +1 -1
  140. package/dist/types/LazyType.d.ts +17 -0
  141. package/dist/types/LazyType.js +44 -0
  142. package/dist/types/LazyType.js.map +1 -0
  143. package/dist/types/LongIntegerType.d.ts +2 -1
  144. package/dist/types/LongIntegerType.js +11 -11
  145. package/dist/types/LongIntegerType.js.map +1 -1
  146. package/dist/types/ObjectType.d.ts +6 -2
  147. package/dist/types/ObjectType.js +9 -3
  148. package/dist/types/ObjectType.js.map +1 -1
  149. package/dist/types/StringType.d.ts +3 -2
  150. package/dist/types/StringType.js +6 -3
  151. package/dist/types/StringType.js.map +1 -1
  152. package/dist/types/UninitializedType.d.ts +4 -2
  153. package/dist/types/UninitializedType.js +8 -3
  154. package/dist/types/UninitializedType.js.map +1 -1
  155. package/dist/types/VoidType.d.ts +3 -2
  156. package/dist/types/VoidType.js +6 -3
  157. package/dist/types/VoidType.js.map +1 -1
  158. package/dist/types/helpers.d.ts +42 -0
  159. package/dist/types/helpers.js +113 -0
  160. package/dist/types/helpers.js.map +1 -0
  161. package/dist/util.d.ts +68 -15
  162. package/dist/util.js +193 -45
  163. package/dist/util.js.map +1 -1
  164. package/dist/validators/ClassValidator.d.ts +5 -1
  165. package/dist/validators/ClassValidator.js +31 -17
  166. package/dist/validators/ClassValidator.js.map +1 -1
  167. package/package.json +1 -1
  168. package/dist/FunctionScope.d.ts +0 -27
  169. package/dist/FunctionScope.js +0 -49
  170. package/dist/FunctionScope.js.map +0 -1
@@ -6,12 +6,8 @@ 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 FunctionScope_1 = require("../FunctionScope");
10
9
  const lexer_1 = require("../lexer");
11
10
  const parser_1 = require("../parser");
12
- const DynamicType_1 = require("../types/DynamicType");
13
- const FunctionType_1 = require("../types/FunctionType");
14
- const VoidType_1 = require("../types/VoidType");
15
11
  const util_1 = require("../util");
16
12
  const BrsTranspileState_1 = require("../parser/BrsTranspileState");
17
13
  const Preprocessor_1 = require("../preprocessor/Preprocessor");
@@ -20,23 +16,36 @@ const serialize_error_1 = require("serialize-error");
20
16
  const reflection_1 = require("../astUtils/reflection");
21
17
  const visitors_1 = require("../astUtils/visitors");
22
18
  const CommentFlagProcessor_1 = require("../CommentFlagProcessor");
19
+ const BscType_1 = require("../types/BscType");
20
+ const UninitializedType_1 = require("../types/UninitializedType");
21
+ const InvalidType_1 = require("../types/InvalidType");
22
+ const globalCallables_1 = require("../globalCallables");
23
+ const DynamicType_1 = require("../types/DynamicType");
23
24
  /**
24
25
  * Holds all details about this file within the scope of the whole program
25
26
  */
26
27
  class BrsFile {
27
- constructor(pathAbsolute,
28
+ constructor(
29
+ /**
30
+ * The absolute path to the source file on disk (e.g. '/usr/you/projects/RokuApp/source/main.brs' or 'c:/projects/RokuApp/source/main.brs').
31
+ */
32
+ srcPath,
28
33
  /**
29
- * The full pkg path to this file
34
+ * The full pkg path (i.e. `pkg:/path/to/file.brs`)
30
35
  */
31
36
  pkgPath, program) {
32
37
  var _a;
33
- this.pathAbsolute = pathAbsolute;
38
+ this.srcPath = srcPath;
34
39
  this.pkgPath = pkgPath;
35
40
  this.program = program;
36
41
  /**
37
42
  * The parseMode used for the parser for this file
38
43
  */
39
44
  this.parseMode = parser_1.ParseMode.BrightScript;
45
+ /**
46
+ * Indicates whether this file needs to be validated.
47
+ */
48
+ this.isValidated = false;
40
49
  this.diagnostics = [];
41
50
  this.commentFlags = [];
42
51
  this.callables = [];
@@ -49,11 +58,9 @@ class BrsFile {
49
58
  * Does this file need to be transpiled?
50
59
  */
51
60
  this.needsTranspiled = false;
52
- this.scopesByFunc = new Map();
53
- this.pathAbsolute = (0, util_1.standardizePath) `${this.pathAbsolute}`;
54
- this.pkgPath = (0, util_1.standardizePath) `${this.pkgPath}`;
61
+ this.srcPath = (0, util_1.standardizePath) `${this.srcPath}`;
55
62
  this.dependencyGraphKey = this.pkgPath.toLowerCase();
56
- this.extension = util_1.util.getExtension(this.pathAbsolute);
63
+ this.extension = util_1.util.getExtension(this.srcPath);
57
64
  //all BrighterScript files need to be transpiled
58
65
  if ((_a = this.extension) === null || _a === void 0 ? void 0 : _a.endsWith('.bs')) {
59
66
  this.needsTranspiled = true;
@@ -61,7 +68,7 @@ class BrsFile {
61
68
  }
62
69
  this.isTypedef = this.extension === '.d.bs';
63
70
  if (!this.isTypedef) {
64
- this.typedefKey = util_1.util.getTypedefPath(this.pathAbsolute);
71
+ this.typedefSrcPath = util_1.util.getTypedefPath(this.srcPath);
65
72
  }
66
73
  //global file doesn't have a program, so only resolve typedef info if we have a program
67
74
  if (this.program) {
@@ -74,29 +81,12 @@ class BrsFile {
74
81
  addDiagnostics(diagnostics) {
75
82
  this.diagnostics.push(...diagnostics);
76
83
  }
77
- get functionScopes() {
78
- if (!this._functionScopes) {
79
- this.createFunctionScopes();
80
- }
81
- return this._functionScopes;
82
- }
83
84
  /**
84
85
  * The AST for this file
85
86
  */
86
87
  get ast() {
87
88
  return this.parser.ast;
88
89
  }
89
- /**
90
- * Get the token at the specified position
91
- * @param position
92
- */
93
- getTokenAt(position) {
94
- for (let token of this.parser.tokens) {
95
- if (util_1.util.rangeContains(token.range, position)) {
96
- return token;
97
- }
98
- }
99
- }
100
90
  get parser() {
101
91
  if (!this._parser) {
102
92
  //remove the typedef file (if it exists)
@@ -113,7 +103,7 @@ class BrsFile {
113
103
  * Find and set the typedef variables (if a matching typedef file exists)
114
104
  */
115
105
  resolveTypedef() {
116
- this.typedefFile = this.program.getFileByPathAbsolute(this.typedefKey);
106
+ this.typedefFile = this.program.getFile(this.typedefSrcPath);
117
107
  this.hasTypedef = !!this.typedefFile;
118
108
  }
119
109
  /**
@@ -148,7 +138,7 @@ class BrsFile {
148
138
  return;
149
139
  }
150
140
  //tokenize the input file
151
- let lexer = this.program.logger.time(Logger_1.LogLevel.debug, ['lexer.lex', chalk_1.default.green(this.pathAbsolute)], () => {
141
+ let lexer = this.program.logger.time(Logger_1.LogLevel.debug, ['lexer.lex', chalk_1.default.green(this.srcPath)], () => {
152
142
  return lexer_1.Lexer.scan(fileContents, {
153
143
  includeWhitespace: false
154
144
  });
@@ -159,7 +149,7 @@ class BrsFile {
159
149
  //TODO preprocessor should go away in favor of the AST handling this internally (because it affects transpile)
160
150
  //currently the preprocessor throws exceptions on syntax errors...so we need to catch it
161
151
  try {
162
- this.program.logger.time(Logger_1.LogLevel.debug, ['preprocessor.process', chalk_1.default.green(this.pathAbsolute)], () => {
152
+ this.program.logger.time(Logger_1.LogLevel.debug, ['preprocessor.process', chalk_1.default.green(this.srcPath)], () => {
163
153
  preprocessor.process(lexer.tokens, this.program.getManifest());
164
154
  });
165
155
  }
@@ -171,7 +161,7 @@ class BrsFile {
171
161
  }
172
162
  //if the preprocessor generated tokens, use them.
173
163
  let tokens = preprocessor.processedTokens.length > 0 ? preprocessor.processedTokens : lexer.tokens;
174
- this.program.logger.time(Logger_1.LogLevel.debug, ['parser.parse', chalk_1.default.green(this.pathAbsolute)], () => {
164
+ this.program.logger.time(Logger_1.LogLevel.debug, ['parser.parse', chalk_1.default.green(this.srcPath)], () => {
175
165
  this._parser = parser_1.Parser.parse(tokens, {
176
166
  mode: this.parseMode,
177
167
  logger: this.program.logger
@@ -179,8 +169,6 @@ class BrsFile {
179
169
  });
180
170
  //absorb all lexing/preprocessing/parsing diagnostics
181
171
  this.diagnostics.push(...lexer.diagnostics, ...preprocessor.diagnostics, ...this._parser.diagnostics);
182
- //notify AST ready
183
- this.program.plugins.emit('afterFileParse', this);
184
172
  //extract all callables from this file
185
173
  this.findCallables();
186
174
  //find all places where a sub/function is being called
@@ -196,6 +184,7 @@ class BrsFile {
196
184
  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
185
  }
198
186
  }
187
+ validate() { }
199
188
  findAndValidateImportAndImportStatements() {
200
189
  var _a;
201
190
  let topOfFileIncludeStatements = [];
@@ -291,160 +280,17 @@ class BrsFile {
291
280
  this.commentFlags.push(...processor.commentFlags);
292
281
  this.diagnostics.push(...processor.diagnostics);
293
282
  }
294
- /**
295
- * Create a scope for every function in this file
296
- */
297
- createFunctionScopes() {
298
- var _a;
299
- //find every function
300
- let functions = this.parser.references.functionExpressions;
301
- //create a functionScope for every function
302
- this._functionScopes = [];
303
- for (let func of functions) {
304
- let scope = new FunctionScope_1.FunctionScope(func);
305
- //find parent function, and add this scope to it if found
306
- {
307
- let parentScope = this.scopesByFunc.get(func.parentFunction);
308
- //add this child scope to its parent
309
- if (parentScope) {
310
- parentScope.childrenScopes.push(scope);
311
- }
312
- //store the parent scope for this scope
313
- scope.parentScope = parentScope;
314
- }
315
- //add every parameter
316
- for (let param of func.parameters) {
317
- scope.variableDeclarations.push({
318
- nameRange: param.name.range,
319
- lineIndex: param.name.range.start.line,
320
- name: param.name.text,
321
- type: param.type
322
- });
323
- }
324
- //add all of ForEachStatement loop varibales
325
- (_a = func.body) === null || _a === void 0 ? void 0 : _a.walk((0, visitors_1.createVisitor)({
326
- ForEachStatement: (stmt) => {
327
- scope.variableDeclarations.push({
328
- nameRange: stmt.item.range,
329
- lineIndex: stmt.item.range.start.line,
330
- name: stmt.item.text,
331
- type: new DynamicType_1.DynamicType()
332
- });
333
- },
334
- LabelStatement: (stmt) => {
335
- const { identifier } = stmt.tokens;
336
- scope.labelStatements.push({
337
- nameRange: identifier.range,
338
- lineIndex: identifier.range.start.line,
339
- name: identifier.text
340
- });
341
- }
342
- }), {
343
- walkMode: visitors_1.WalkMode.visitStatements
344
- });
345
- this.scopesByFunc.set(func, scope);
346
- //find every statement in the scope
347
- this._functionScopes.push(scope);
348
- }
349
- //find every variable assignment in the whole file
350
- let assignmentStatements = this.parser.references.assignmentStatements;
351
- for (let statement of assignmentStatements) {
352
- //find this statement's function scope
353
- let scope = this.scopesByFunc.get(statement.containingFunction);
354
- //skip variable declarations that are outside of any scope
355
- if (scope) {
356
- scope.variableDeclarations.push({
357
- nameRange: statement.name.range,
358
- lineIndex: statement.name.range.start.line,
359
- name: statement.name.text,
360
- type: this.getBscTypeFromAssignment(statement, scope)
361
- });
362
- }
363
- }
364
- }
365
- getBscTypeFromAssignment(assignment, scope) {
366
- var _a, _b, _c, _d;
367
- try {
368
- //function
369
- if ((0, reflection_1.isFunctionExpression)(assignment.value)) {
370
- let functionType = new FunctionType_1.FunctionType(assignment.value.returnType);
371
- functionType.isSub = assignment.value.functionType.text === 'sub';
372
- if (functionType.isSub) {
373
- functionType.returnType = new VoidType_1.VoidType();
374
- }
375
- functionType.setName(assignment.name.text);
376
- for (let param of assignment.value.parameters) {
377
- let isRequired = !param.defaultValue;
378
- //TODO compute optional parameters
379
- functionType.addParameter(param.name.text, param.type, isRequired);
380
- }
381
- return functionType;
382
- //literal
383
- }
384
- else if ((0, reflection_1.isLiteralExpression)(assignment.value)) {
385
- return assignment.value.type;
386
- //function call
387
- }
388
- else if ((0, reflection_1.isCallExpression)(assignment.value)) {
389
- let calleeName = (_b = (_a = assignment.value.callee) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.text;
390
- if (calleeName) {
391
- let func = this.getCallableByName(calleeName);
392
- if (func) {
393
- return func.type.returnType;
394
- }
395
- }
396
- }
397
- else if ((0, reflection_1.isVariableExpression)(assignment.value)) {
398
- let variableName = (_d = (_c = assignment.value) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.text;
399
- let variable = scope.getVariableByName(variableName);
400
- return variable.type;
401
- }
402
- }
403
- catch (e) {
404
- //do nothing. Just return dynamic
405
- }
406
- //fallback to dynamic
407
- return new DynamicType_1.DynamicType();
408
- }
409
- getCallableByName(name) {
410
- name = name ? name.toLowerCase() : undefined;
411
- if (!name) {
412
- return;
413
- }
414
- for (let func of this.callables) {
415
- if (func.name.toLowerCase() === name) {
416
- return func;
417
- }
418
- }
419
- }
420
283
  findCallables() {
421
284
  var _a;
422
285
  for (let statement of (_a = this.parser.references.functionStatements) !== null && _a !== void 0 ? _a : []) {
423
- let functionType = new FunctionType_1.FunctionType(statement.func.returnType);
286
+ let functionType = statement.func.getFunctionType();
424
287
  functionType.setName(statement.name.text);
425
- functionType.isSub = statement.func.functionType.text.toLowerCase() === 'sub';
426
- if (functionType.isSub) {
427
- functionType.returnType = new VoidType_1.VoidType();
428
- }
429
- //extract the parameters
430
- let params = [];
431
- for (let param of statement.func.parameters) {
432
- let callableParam = {
433
- name: param.name.text,
434
- type: param.type,
435
- isOptional: !!param.defaultValue,
436
- isRestArgument: false
437
- };
438
- params.push(callableParam);
439
- let isRequired = !param.defaultValue;
440
- functionType.addParameter(callableParam.name, callableParam.type, isRequired);
441
- }
442
288
  this.callables.push({
443
289
  isSub: statement.func.functionType.text.toLowerCase() === 'sub',
444
290
  name: statement.name.text,
445
291
  nameRange: statement.name.range,
446
292
  file: this,
447
- params: params,
293
+ params: functionType.params,
448
294
  range: statement.func.range,
449
295
  type: functionType,
450
296
  getName: statement.getName.bind(statement),
@@ -454,21 +300,24 @@ class BrsFile {
454
300
  }
455
301
  }
456
302
  findFunctionCalls() {
303
+ var _a;
457
304
  this.functionCalls = [];
458
305
  //for every function in the file
459
306
  for (let func of this._parser.references.functionExpressions) {
460
307
  //for all function calls in this function
461
308
  for (let expression of func.callExpressions) {
462
309
  if (
463
- //filter out dotted function invocations (i.e. object.doSomething()) (not currently supported. TODO support it)
464
- expression.callee.obj ||
465
- //filter out method calls on method calls for now (i.e. getSomething().getSomethingElse())
466
- expression.callee.callee ||
310
+ //filter out method calls on method calls for now (i.e. getSomething().getSomethingElse())
311
+ expression.callee.callee ||
312
+ //filter out method calls on regexp literals for now
313
+ (0, reflection_1.isRegexLiteralExpression)((_a = expression.callee) === null || _a === void 0 ? void 0 : _a.obj) ||
467
314
  //filter out callees without a name (immediately-invoked function expressions)
468
315
  !expression.callee.name) {
469
316
  continue;
470
317
  }
471
- let functionName = expression.callee.name.text;
318
+ //Flag dotted function invocations (i.e. object.doSomething())
319
+ const dottedInvocation = expression.callee.obj;
320
+ let functionName = expression.callee.name;
472
321
  //callee is the name of the function being called
473
322
  let callee = expression.callee;
474
323
  let columnIndexBegin = callee.range.start.character;
@@ -476,81 +325,65 @@ class BrsFile {
476
325
  let args = [];
477
326
  //TODO convert if stmts to use instanceof instead
478
327
  for (let arg of expression.args) {
479
- //is a literal parameter value
480
- if ((0, reflection_1.isLiteralExpression)(arg)) {
481
- args.push({
482
- range: arg.range,
483
- type: arg.type,
484
- text: arg.token.text
485
- });
486
- //is variable being passed into argument
328
+ let inferredType = (0, parser_1.getBscTypeFromExpression)(arg, func);
329
+ let argText = '';
330
+ // Get the text to display for the arg
331
+ if (arg.token) {
332
+ argText = arg.token.text;
333
+ //is a function call being passed into argument
487
334
  }
488
335
  else if (arg.name) {
489
- args.push({
490
- range: arg.range,
491
- //TODO - look up the data type of the actual variable
492
- type: new DynamicType_1.DynamicType(),
493
- text: arg.name.text
494
- });
336
+ if ((0, lexer_1.isToken)(arg.name)) {
337
+ argText = arg.name.text;
338
+ }
495
339
  }
496
340
  else if (arg.value) {
497
- let text = '';
498
341
  /* istanbul ignore next: TODO figure out why value is undefined sometimes */
499
342
  if (arg.value.value) {
500
- text = arg.value.value.toString();
343
+ if (arg.value.value.toString) {
344
+ argText = arg.value.value.toString();
345
+ }
501
346
  }
502
- let callableArg = {
503
- range: arg.range,
504
- //TODO not sure what to do here
505
- type: new DynamicType_1.DynamicType(),
506
- text: text
507
- };
508
347
  //wrap the value in quotes because that's how it appears in the code
509
- if ((0, reflection_1.isStringType)(callableArg.type)) {
510
- callableArg.text = '"' + callableArg.text + '"';
348
+ if (argText && (0, reflection_1.isStringType)(inferredType)) {
349
+ argText = '"' + argText + '"';
511
350
  }
512
- args.push(callableArg);
513
- }
514
- else {
515
- args.push({
516
- range: arg.range,
517
- type: new DynamicType_1.DynamicType(),
518
- //TODO get text from other types of args
519
- text: ''
520
- });
521
351
  }
352
+ args.push({
353
+ range: arg.range,
354
+ type: inferredType,
355
+ text: argText
356
+ });
522
357
  }
523
358
  let functionCall = {
524
359
  range: util_1.util.createRangeFromPositions(expression.range.start, expression.closingParen.range.end),
525
- functionScope: this.getFunctionScopeAtPosition(callee.range.start),
360
+ functionExpression: this.getFunctionExpressionAtPosition(callee.range.start),
526
361
  file: this,
527
362
  name: functionName,
528
363
  nameRange: util_1.util.createRange(callee.range.start.line, columnIndexBegin, callee.range.start.line, columnIndexEnd),
529
- //TODO keep track of parameters
530
- args: args
364
+ args: args,
365
+ isDottedInvocation: dottedInvocation
531
366
  };
532
367
  this.functionCalls.push(functionCall);
533
368
  }
534
369
  }
535
370
  }
536
371
  /**
537
- * Find the function scope at the given position.
538
- * @param position
539
- * @param functionScopes
372
+ * Find the function expression at the given position.
540
373
  */
541
- getFunctionScopeAtPosition(position, functionScopes) {
542
- if (!functionScopes) {
543
- functionScopes = this.functionScopes;
374
+ getFunctionExpressionAtPosition(position, functionExpressions) {
375
+ if (!functionExpressions) {
376
+ functionExpressions = this.parser.references.functionExpressions;
544
377
  }
545
- for (let scope of functionScopes) {
546
- if (util_1.util.rangeContains(scope.range, position)) {
378
+ for (let functionExpression of functionExpressions) {
379
+ if (util_1.util.rangeContains(functionExpression.range, position)) {
547
380
  //see if any of that scope's children match the position also, and give them priority
548
- let childScope = this.getFunctionScopeAtPosition(position, scope.childrenScopes);
549
- if (childScope) {
550
- return childScope;
381
+ let childFunc = this.getFunctionExpressionAtPosition(position, functionExpression.childFunctionExpressions);
382
+ if (childFunc) {
383
+ return childFunc;
551
384
  }
552
385
  else {
553
- return scope;
386
+ return functionExpression;
554
387
  }
555
388
  }
556
389
  }
@@ -559,6 +392,7 @@ class BrsFile {
559
392
  * Get completions available at the given cursor. This aggregates all values from this file and the current scope.
560
393
  */
561
394
  getCompletions(position, scope) {
395
+ var _a;
562
396
  let result = [];
563
397
  //a map of lower-case names of all added options
564
398
  let names = {};
@@ -568,7 +402,7 @@ class BrsFile {
568
402
  return this.program.getScriptImportCompletions(this.pkgPath, scriptImport);
569
403
  }
570
404
  //if cursor is within a comment, disable completions
571
- let currentToken = this.getTokenAt(position);
405
+ let currentToken = this.parser.getTokenAt(position);
572
406
  const tokenKind = currentToken === null || currentToken === void 0 ? void 0 : currentToken.kind;
573
407
  if (tokenKind === lexer_1.TokenKind.Comment) {
574
408
  return [];
@@ -577,9 +411,9 @@ class BrsFile {
577
411
  const match = /^("?)(pkg|libpkg):/.exec(currentToken.text);
578
412
  if (match) {
579
413
  const [, openingQuote, fileProtocol] = match;
580
- //include every absolute file path from this scope
414
+ //include every pkgPath from this scope
581
415
  for (const file of scope.getAllFiles()) {
582
- const pkgPath = `${fileProtocol}:/${file.pkgPath.replace(/\\/g, '/')}`;
416
+ const pkgPath = `${fileProtocol}:/${file.pkgPath.replace('pkg:/', '')}`;
583
417
  result.push({
584
418
  label: pkgPath,
585
419
  textEdit: vscode_languageserver_1.TextEdit.replace(util_1.util.createRange(currentToken.range.start.line,
@@ -602,10 +436,10 @@ class BrsFile {
602
436
  return namespaceCompletions;
603
437
  }
604
438
  //determine if cursor is inside a function
605
- let functionScope = this.getFunctionScopeAtPosition(position);
606
- if (!functionScope) {
439
+ let functionExpression = this.getFunctionExpressionAtPosition(position);
440
+ if (!functionExpression) {
607
441
  //we aren't in any function scope, so return the keyword completions and namespaces
608
- if (this.getTokenBefore(currentToken, lexer_1.TokenKind.New)) {
442
+ if (this.parser.getTokenBefore(currentToken, lexer_1.TokenKind.New)) {
609
443
  // there's a new keyword, so only class types are viable here
610
444
  return [...this.getGlobalClassStatementCompletions(currentToken, this.parseMode)];
611
445
  }
@@ -614,26 +448,31 @@ class BrsFile {
614
448
  }
615
449
  }
616
450
  const classNameCompletions = this.getGlobalClassStatementCompletions(currentToken, this.parseMode);
617
- const newToken = this.getTokenBefore(currentToken, lexer_1.TokenKind.New);
451
+ const newToken = this.parser.getTokenBefore(currentToken, lexer_1.TokenKind.New);
618
452
  if (newToken) {
619
453
  //we are after a new keyword; so we can only be namespaces or classes at this point
620
454
  result.push(...classNameCompletions);
621
455
  result.push(...namespaceCompletions);
622
456
  return result;
623
457
  }
624
- if (this.tokenFollows(currentToken, lexer_1.TokenKind.Goto)) {
625
- return this.getLabelCompletion(functionScope);
458
+ if (this.parser.tokenFollows(currentToken, lexer_1.TokenKind.Goto)) {
459
+ return this.getLabelCompletion(functionExpression);
626
460
  }
627
- if (this.isPositionNextToTokenKind(position, lexer_1.TokenKind.Dot)) {
461
+ if (this.parser.isPositionNextToTokenKind(position, lexer_1.TokenKind.Dot)) {
628
462
  if (namespaceCompletions.length > 0) {
629
463
  //if we matched a namespace, after a dot, it can't be anything else but something from our namespace completions
630
464
  return namespaceCompletions;
631
465
  }
632
- const selfClassMemberCompletions = this.getClassMemberCompletions(position, currentToken, functionScope, scope);
466
+ const selfClassMemberCompletions = this.getClassMemberCompletions(position, currentToken, functionExpression, scope);
633
467
  if (selfClassMemberCompletions.size > 0) {
634
468
  return [...selfClassMemberCompletions.values()].filter((i) => i.label !== 'new');
635
469
  }
636
- if (!this.getClassFromMReference(position, currentToken, functionScope)) {
470
+ const tokenLookup = this.getSymbolTypeFromToken(currentToken, functionExpression, scope);
471
+ if ((_a = tokenLookup.symbolContainer) === null || _a === void 0 ? void 0 : _a.memberTable) {
472
+ return this.getCompletionsFromSymbolTable(tokenLookup.symbolContainer.memberTable);
473
+ }
474
+ const foundClassLink = this.getClassFromTokenLookup(tokenLookup, scope);
475
+ if (!foundClassLink) {
637
476
  //and anything from any class in scope to a non m class
638
477
  let classMemberCompletions = scope.getAllClassMemberCompletions();
639
478
  result.push(...classMemberCompletions.values());
@@ -658,16 +497,21 @@ class BrsFile {
658
497
  names.m = true;
659
498
  result.push(...exports.KeywordCompletions);
660
499
  //include local variables
661
- let variables = functionScope.variableDeclarations;
662
- for (let variable of variables) {
500
+ for (let symbol of functionExpression.symbolTable.getOwnSymbols()) {
501
+ const symbolNameLower = symbol.name.toLowerCase();
663
502
  //skip duplicate variable names
664
- if (names[variable.name.toLowerCase()]) {
503
+ if (names[symbolNameLower]) {
665
504
  continue;
666
505
  }
667
- names[variable.name.toLowerCase()] = true;
506
+ names[symbolNameLower] = true;
507
+ // TODO TYPES (This may be a performance hit?)
508
+ // const foundType = getTypeFromContext(symbol.type, { scope: scope, file: this });
668
509
  result.push({
669
- label: variable.name,
670
- kind: (0, reflection_1.isFunctionType)(variable.type) ? vscode_languageserver_1.CompletionItemKind.Function : vscode_languageserver_1.CompletionItemKind.Variable
510
+ //TODO does this work?
511
+ label: symbol.name,
512
+ //TODO TYPES find type for local vars - SEE above
513
+ kind: vscode_languageserver_1.CompletionItemKind.Variable
514
+ // kind: isFunctionType(foundType) ? CompletionItemKind.Function : CompletionItemKind.Variable
671
515
  });
672
516
  }
673
517
  if (this.parseMode === parser_1.ParseMode.BrighterScript) {
@@ -689,15 +533,23 @@ class BrsFile {
689
533
  }
690
534
  return result;
691
535
  }
692
- getLabelCompletion(functionScope) {
693
- return functionScope.labelStatements.map(label => ({
694
- label: label.name,
536
+ getCompletionsFromSymbolTable(symbolTable) {
537
+ return symbolTable.getAllSymbols().map(bscType => {
538
+ return {
539
+ label: bscType.name,
540
+ kind: (0, reflection_1.isFunctionType)(bscType.type) ? vscode_languageserver_1.CompletionItemKind.Method : vscode_languageserver_1.CompletionItemKind.Field
541
+ };
542
+ });
543
+ }
544
+ getLabelCompletion(func) {
545
+ return func.labelStatements.map(label => ({
546
+ label: label.tokens.identifier.text,
695
547
  kind: vscode_languageserver_1.CompletionItemKind.Reference
696
548
  }));
697
549
  }
698
- getClassMemberCompletions(position, currentToken, functionScope, scope) {
550
+ getClassMemberCompletions(position, currentToken, functionExpression, scope) {
699
551
  var _a, _b, _c, _d;
700
- let classStatement = this.getClassFromMReference(position, currentToken, functionScope);
552
+ let classStatement = this.getClassFromToken(currentToken, functionExpression, scope);
701
553
  let results = new Map();
702
554
  if (classStatement) {
703
555
  let classes = scope.getClassHierarchy(classStatement.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
@@ -714,16 +566,265 @@ class BrsFile {
714
566
  }
715
567
  return results;
716
568
  }
717
- getClassFromMReference(position, currentToken, functionScope) {
718
- let previousToken = this.getPreviousToken(currentToken);
719
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Dot) {
720
- previousToken = this.getPreviousToken(previousToken);
569
+ /**
570
+ * Gets the class (if any) of a given token based on the scope
571
+ * @param currentToken token in question
572
+ * @param functionExpression current functionExpression
573
+ * @param scope the current scope
574
+ * @returns A fileLink of the ClassStatement, if it is a class, otherwise undefined
575
+ */
576
+ getClassFromToken(currentToken, functionExpression, scope) {
577
+ const tokenLookup = this.getSymbolTypeFromToken(currentToken, functionExpression, scope);
578
+ return this.getClassFromTokenLookup(tokenLookup, scope);
579
+ }
580
+ /**
581
+ * Gets the class (if any) of a given token based on the scope
582
+ * @param currentToken token in question
583
+ * @param functionExpression current functionExpression
584
+ * @param scope the current scope
585
+ * @returns A fileLink of the ClassStatement, if it is a class, otherwise undefined
586
+ */
587
+ getClassFromTokenLookup(tokenLookup, scope) {
588
+ const currentClass = tokenLookup === null || tokenLookup === void 0 ? void 0 : tokenLookup.symbolContainer;
589
+ if ((0, reflection_1.isClassStatement)(currentClass)) {
590
+ return { item: currentClass, file: this };
721
591
  }
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)) {
723
- return { item: this.parser.references.classStatements.find((cs) => util_1.util.rangeContains(cs.range, position)), file: this };
592
+ else if ((0, reflection_1.isCustomType)(currentClass)) {
593
+ const foundClass = scope.getClass(currentClass.name);
594
+ if (foundClass) {
595
+ return { item: foundClass, file: this };
596
+ }
724
597
  }
725
598
  return undefined;
726
599
  }
600
+ findNamespaceFromTokenChain(originalTokenChain, scope) {
601
+ let namespaceTokens = [];
602
+ let startsWithNamespace = '';
603
+ let namespaceContainer;
604
+ let tokenChain = [...originalTokenChain];
605
+ while (tokenChain[0] && tokenChain[0].usage === parser_1.TokenUsage.Direct) {
606
+ const namespaceNameToCheck = `${startsWithNamespace}${startsWithNamespace.length > 0 ? '.' : ''}${tokenChain[0].token.text}`.toLowerCase();
607
+ const foundNamespace = scope.namespaceLookup[namespaceNameToCheck];
608
+ if (foundNamespace) {
609
+ namespaceContainer = foundNamespace;
610
+ namespaceTokens.push(tokenChain[0].token);
611
+ startsWithNamespace = namespaceTokens.map(token => token.text).join('.');
612
+ tokenChain.shift();
613
+ }
614
+ else {
615
+ break;
616
+ }
617
+ }
618
+ if (namespaceTokens.length > 0) {
619
+ namespaceContainer = scope.namespaceLookup[startsWithNamespace.toLowerCase()];
620
+ }
621
+ return { namespaceContainer: namespaceContainer, tokenChain: tokenChain };
622
+ }
623
+ checkForSpecialClassSymbol(currentToken, scope, func) {
624
+ var _a;
625
+ const containingClass = this.parser.getContainingClass(currentToken);
626
+ let symbolType;
627
+ let currentClassRef;
628
+ const currentTokenLower = currentToken.text.toLowerCase();
629
+ const typeContext = { file: this, scope: scope, position: currentToken.range.start };
630
+ if (containingClass) {
631
+ // Special cases for a single token inside a class
632
+ let expandedText = '';
633
+ let useExpandedTextOnly = false;
634
+ if (containingClass.name === currentToken) {
635
+ symbolType = containingClass.getCustomType();
636
+ expandedText = `class ${containingClass.getName(parser_1.ParseMode.BrighterScript)}`;
637
+ useExpandedTextOnly = true;
638
+ currentClassRef = containingClass;
639
+ }
640
+ else if (currentTokenLower === 'm') {
641
+ symbolType = containingClass.getCustomType();
642
+ expandedText = currentToken.text;
643
+ currentClassRef = containingClass;
644
+ }
645
+ else if (currentTokenLower === 'super') {
646
+ symbolType = (0, BscType_1.getTypeFromContext)(containingClass.symbolTable.getSymbolType(currentTokenLower, true, typeContext), typeContext);
647
+ if ((0, reflection_1.isFunctionType)(symbolType)) {
648
+ currentClassRef = scope.getParentClass(containingClass);
649
+ }
650
+ }
651
+ else if (((_a = func === null || func === void 0 ? void 0 : func.functionStatement) === null || _a === void 0 ? void 0 : _a.name) === currentToken) {
652
+ // check if this is a method declaration
653
+ currentClassRef = containingClass;
654
+ 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('.');
656
+ }
657
+ else if (!func) {
658
+ // check if this is a field declaration
659
+ currentClassRef = containingClass;
660
+ 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('.');
662
+ }
663
+ if (symbolType) {
664
+ return { type: symbolType, expandedTokenText: expandedText, symbolContainer: currentClassRef, useExpandedTextOnly: useExpandedTextOnly };
665
+ }
666
+ }
667
+ }
668
+ checkForSpecialCaseToken(nameSpacedTokenChain, functionExpression, scope) {
669
+ var _a;
670
+ const tokenChain = (_a = nameSpacedTokenChain.tokenChain) !== null && _a !== void 0 ? _a : [];
671
+ if (nameSpacedTokenChain.namespaceContainer && tokenChain.length === 0) {
672
+ //currentToken was part of a namespace
673
+ return {
674
+ type: null,
675
+ expandedTokenText: `namespace ${nameSpacedTokenChain.namespaceContainer.fullName}`,
676
+ useExpandedTextOnly: true
677
+ };
678
+ }
679
+ const specialCase = tokenChain.length === 1 ? this.checkForSpecialClassSymbol(tokenChain[0].token, scope, functionExpression) : null;
680
+ if (specialCase) {
681
+ return specialCase;
682
+ }
683
+ }
684
+ /**
685
+ * Checks previous tokens for the start of a symbol chain (eg. m.property.subProperty.method())
686
+ * @param currentToken The token to check
687
+ * @param functionExpression The current function context
688
+ * @param scope use this scope for finding class maps
689
+ * @returns the BscType, expanded text (e.g <Class.field>) and classStatement (if available) for the token
690
+ */
691
+ getSymbolTypeFromToken(currentToken, functionExpression, scope) {
692
+ var _a, _b, _c, _d, _e;
693
+ if (!scope) {
694
+ return undefined;
695
+ }
696
+ const cachedSymbolData = scope.symbolCache.get(currentToken);
697
+ if (cachedSymbolData) {
698
+ return cachedSymbolData;
699
+ }
700
+ const tokenChainResponse = this.parser.getTokenChain(currentToken);
701
+ if (tokenChainResponse.includesUnknowableTokenType) {
702
+ const symbolData = { type: new DynamicType_1.DynamicType(), expandedTokenText: currentToken.text };
703
+ scope.symbolCache.set(currentToken, symbolData);
704
+ return symbolData;
705
+ }
706
+ const nameSpacedTokenChain = this.findNamespaceFromTokenChain(tokenChainResponse.chain, scope);
707
+ const specialCase = this.checkForSpecialCaseToken(nameSpacedTokenChain, functionExpression, scope);
708
+ if (specialCase) {
709
+ scope.symbolCache.set(currentToken, specialCase);
710
+ return specialCase;
711
+ }
712
+ const tokenChain = nameSpacedTokenChain.tokenChain;
713
+ let symbolContainer = this.parser.getContainingAA(currentToken) || this.parser.getContainingClass(currentToken);
714
+ let currentSymbolTable = (_b = (_a = nameSpacedTokenChain.namespaceContainer) === null || _a === void 0 ? void 0 : _a.symbolTable) !== null && _b !== void 0 ? _b : functionExpression === null || functionExpression === void 0 ? void 0 : functionExpression.symbolTable;
715
+ let tokenFoundCount = 0;
716
+ let symbolTypeBeforeReference;
717
+ let symbolType;
718
+ let tokenText = [];
719
+ let justReturnDynamic = false;
720
+ const typeContext = { file: this, scope: scope, position: (_c = tokenChain[0]) === null || _c === void 0 ? void 0 : _c.token.range.start };
721
+ for (const tokenChainMember of tokenChain) {
722
+ const token = tokenChainMember === null || tokenChainMember === void 0 ? void 0 : tokenChainMember.token;
723
+ const tokenLowerText = token.text.toLowerCase();
724
+ if (tokenLowerText === 'super' && (0, reflection_1.isClassStatement)(symbolContainer) && tokenFoundCount === 0) {
725
+ /// Special cases for first item in chain inside a class
726
+ symbolContainer = scope === null || scope === void 0 ? void 0 : scope.getParentClass(symbolContainer);
727
+ currentSymbolTable = (_d = symbolContainer) === null || _d === void 0 ? void 0 : _d.memberTable;
728
+ if (symbolContainer && currentSymbolTable) {
729
+ tokenText.push(symbolContainer.getName(parser_1.ParseMode.BrighterScript));
730
+ tokenFoundCount++;
731
+ continue;
732
+ }
733
+ }
734
+ if (!currentSymbolTable) {
735
+ // uh oh... no symbol table to continue to check
736
+ break;
737
+ }
738
+ symbolType = currentSymbolTable.getSymbolType(tokenLowerText, true, typeContext);
739
+ if (tokenFoundCount === 0 && !symbolType) {
740
+ //check for global callable
741
+ symbolType = (_e = globalCallables_1.globalCallableMap.get(tokenLowerText)) === null || _e === void 0 ? void 0 : _e.type;
742
+ }
743
+ if (symbolType) {
744
+ // found this symbol, and it's valid. increase found counter
745
+ tokenFoundCount++;
746
+ }
747
+ symbolTypeBeforeReference = symbolType;
748
+ if ((0, reflection_1.isFunctionType)(symbolType)) {
749
+ // this is a function, and it is in the start or middle of the chain
750
+ // the next symbol to check will be the return value of this function
751
+ symbolType = (0, BscType_1.getTypeFromContext)(symbolType.returnType, typeContext);
752
+ if (tokenFoundCount < tokenChain.length) {
753
+ // We're still
754
+ symbolTypeBeforeReference = symbolType;
755
+ }
756
+ }
757
+ if (symbolType === null || symbolType === void 0 ? void 0 : symbolType.memberTable) {
758
+ if ((0, reflection_1.isCustomType)(symbolType)) {
759
+ // we're currently looking at a customType, that has it's own symbol table
760
+ // use the name of the custom type
761
+ // TODO TYPES: get proper parent name for methods/fields defined in super classes
762
+ tokenText.push(tokenChain.length === 1 ? token.text : symbolType.name);
763
+ }
764
+ else {
765
+ justReturnDynamic = true;
766
+ tokenText.push(token.text);
767
+ }
768
+ symbolContainer = symbolType;
769
+ currentSymbolTable = symbolContainer === null || symbolContainer === void 0 ? void 0 : symbolContainer.memberTable;
770
+ }
771
+ else if ((0, reflection_1.isObjectType)(symbolType) || (0, reflection_1.isArrayType)(symbolType) || (0, reflection_1.isDynamicType)(symbolType)) {
772
+ // this is an object that has no member table
773
+ // this could happen if a parameter is marked as object
774
+ // assume all fields are dynamic
775
+ symbolContainer = undefined;
776
+ tokenText.push(token.text);
777
+ justReturnDynamic = true;
778
+ break;
779
+ }
780
+ else {
781
+ // No further symbol tables were found
782
+ symbolContainer = undefined;
783
+ tokenText.push(token.text);
784
+ break;
785
+ }
786
+ if (tokenText.length > 2) {
787
+ tokenText.shift(); // only care about last two symbols
788
+ }
789
+ }
790
+ let expandedTokenText = tokenText.join('.');
791
+ let backUpReturnType;
792
+ if (tokenFoundCount === tokenChain.length) {
793
+ // did we complete the chain? if so, we have a valid token at the end
794
+ const symbolData = { type: symbolTypeBeforeReference, expandedTokenText: tokenText.join('.'), symbolContainer: symbolContainer };
795
+ scope.symbolCache.set(currentToken, symbolData);
796
+ return symbolData;
797
+ }
798
+ if ((0, reflection_1.isDynamicType)(symbolTypeBeforeReference) || (0, reflection_1.isArrayType)(symbolTypeBeforeReference) || justReturnDynamic) {
799
+ // last type in chain is dynamic... so currentToken could be anything.
800
+ backUpReturnType = new DynamicType_1.DynamicType();
801
+ expandedTokenText = currentToken.text;
802
+ }
803
+ else if ((0, reflection_1.isPrimitiveType)(symbolTypeBeforeReference)) {
804
+ // last type in chain is dynamic... so currentToken could be anything.
805
+ backUpReturnType = new DynamicType_1.DynamicType();
806
+ expandedTokenText = currentToken.text;
807
+ }
808
+ else if (tokenChain.length === 1) {
809
+ // variable that has not been assigned
810
+ expandedTokenText = currentToken.text;
811
+ backUpReturnType = new UninitializedType_1.UninitializedType();
812
+ }
813
+ else if (tokenFoundCount === tokenChain.length - 1) {
814
+ // member field that is not known
815
+ if (symbolContainer) {
816
+ backUpReturnType = new InvalidType_1.InvalidType();
817
+ }
818
+ else {
819
+ // TODO TYPES: once we have stricter object/node member type checking, we could say this is invalid, but until then, call it dynamic
820
+ backUpReturnType = new DynamicType_1.DynamicType();
821
+ expandedTokenText = currentToken.text;
822
+ }
823
+ }
824
+ const symbolData = { type: backUpReturnType, expandedTokenText: expandedTokenText };
825
+ scope.symbolCache.set(currentToken, symbolData);
826
+ return symbolData;
827
+ }
727
828
  getGlobalClassStatementCompletions(currentToken, parseMode) {
728
829
  var _a;
729
830
  if (parseMode === parser_1.ParseMode.BrightScript) {
@@ -762,7 +863,7 @@ class BrsFile {
762
863
  //remove any trailing identifer and then any trailing dot, to give us the
763
864
  //name of its immediate parent namespace
764
865
  let closestParentNamespaceName = completionName.replace(/\.([a-z0-9_]*)?$/gi, '');
765
- let newToken = this.getTokenBefore(currentToken, lexer_1.TokenKind.New);
866
+ let newToken = this.parser.getTokenBefore(currentToken, lexer_1.TokenKind.New);
766
867
  let namespaceLookup = scope.namespaceLookup;
767
868
  let result = new Map();
768
869
  for (let key in namespaceLookup) {
@@ -821,7 +922,7 @@ class BrsFile {
821
922
  if (!location && statement.getName(parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
822
923
  const namespaceItemStatementHandler = (statement) => {
823
924
  if (!location && statement.name.text.toLowerCase() === endName) {
824
- const uri = util_1.util.pathToUri(file.pathAbsolute);
925
+ const uri = util_1.util.pathToUri(file.srcPath);
825
926
  location = vscode_languageserver_1.Location.create(uri, statement.range);
826
927
  }
827
928
  };
@@ -865,64 +966,6 @@ class BrsFile {
865
966
  return undefined;
866
967
  }
867
968
  }
868
- isPositionNextToTokenKind(position, tokenKind) {
869
- const closestToken = this.getClosestToken(position);
870
- const previousToken = this.getPreviousToken(closestToken);
871
- const previousTokenKind = previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind;
872
- //next to matched token
873
- if (!closestToken || closestToken.kind === lexer_1.TokenKind.Eof) {
874
- return false;
875
- }
876
- else if (closestToken.kind === tokenKind) {
877
- return true;
878
- }
879
- else if (closestToken.kind === lexer_1.TokenKind.Newline || previousTokenKind === lexer_1.TokenKind.Newline) {
880
- return false;
881
- //next to an identifier, which is next to token kind
882
- }
883
- else if (closestToken.kind === lexer_1.TokenKind.Identifier && previousTokenKind === tokenKind) {
884
- return true;
885
- }
886
- else {
887
- return false;
888
- }
889
- }
890
- getTokenBefore(currentToken, tokenKind) {
891
- const index = this.parser.tokens.indexOf(currentToken);
892
- for (let i = index - 1; i >= 0; i--) {
893
- currentToken = this.parser.tokens[i];
894
- if (currentToken.kind === lexer_1.TokenKind.Newline) {
895
- break;
896
- }
897
- else if (currentToken.kind === tokenKind) {
898
- return currentToken;
899
- }
900
- }
901
- return undefined;
902
- }
903
- tokenFollows(currentToken, tokenKind) {
904
- const index = this.parser.tokens.indexOf(currentToken);
905
- if (index > 0) {
906
- return this.parser.tokens[index - 1].kind === tokenKind;
907
- }
908
- return false;
909
- }
910
- getTokensUntil(currentToken, tokenKind, direction = -1) {
911
- let tokens = [];
912
- for (let i = this.parser.tokens.indexOf(currentToken); direction === -1 ? i >= 0 : i === this.parser.tokens.length; i += direction) {
913
- currentToken = this.parser.tokens[i];
914
- if (currentToken.kind === lexer_1.TokenKind.Newline || currentToken.kind === tokenKind) {
915
- break;
916
- }
917
- tokens.push(currentToken);
918
- }
919
- return tokens;
920
- }
921
- getPreviousToken(token) {
922
- const parser = this.parser;
923
- let idx = parser.tokens.indexOf(token);
924
- return parser.tokens[idx - 1];
925
- }
926
969
  /**
927
970
  * Find the first scope that has a namespace with this name.
928
971
  * Returns false if no namespace was found with that name
@@ -964,28 +1007,6 @@ class BrsFile {
964
1007
  }
965
1008
  return false;
966
1009
  }
967
- /**
968
- * Get the token closest to the position. if no token is found, the previous token is returned
969
- * @param position
970
- * @param tokens
971
- */
972
- getClosestToken(position) {
973
- let tokens = this.parser.tokens;
974
- for (let i = 0; i < tokens.length; i++) {
975
- let token = tokens[i];
976
- if (util_1.util.rangeContains(token.range, position)) {
977
- return token;
978
- }
979
- //if the position less than this token range, then this position touches no token,
980
- if (util_1.util.positionIsGreaterThanRange(position, token.range) === false) {
981
- let t = tokens[i - 1];
982
- //return the token or the first token
983
- return t ? t : tokens[0];
984
- }
985
- }
986
- //return the last token
987
- return tokens[tokens.length - 1];
988
- }
989
1010
  /**
990
1011
  * Builds a list of document symbols for this file. Used by LanguageServer's onDocumentSymbol functionality
991
1012
  */
@@ -1090,7 +1111,7 @@ class BrsFile {
1090
1111
  return symbols;
1091
1112
  }
1092
1113
  const name = statement.getName(parser_1.ParseMode.BrighterScript);
1093
- const uri = util_1.util.pathToUri(this.pathAbsolute);
1114
+ const uri = util_1.util.pathToUri(this.srcPath);
1094
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));
1095
1116
  symbols.push(symbol);
1096
1117
  return symbols;
@@ -1102,7 +1123,7 @@ class BrsFile {
1102
1123
  getDefinition(position) {
1103
1124
  let results = [];
1104
1125
  //get the token at the position
1105
- const token = this.getTokenAt(position);
1126
+ const token = this.parser.getTokenAt(position);
1106
1127
  // 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
1128
  let definitionTokenTypes = [
1108
1129
  lexer_1.TokenKind.Identifier,
@@ -1113,25 +1134,25 @@ class BrsFile {
1113
1134
  return results;
1114
1135
  }
1115
1136
  let textToSearchFor = token.text.toLowerCase();
1116
- const previousToken = this.getTokenAt({ line: token.range.start.line, character: token.range.start.character });
1137
+ const previousToken = this.parser.getTokenAt({ line: token.range.start.line, character: token.range.start.character });
1117
1138
  if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Callfunc) {
1118
1139
  for (const scope of this.program.getScopes()) {
1119
1140
  //to only get functions defined in interface methods
1120
1141
  const callable = scope.getAllCallables().find((c) => c.callable.name.toLowerCase() === textToSearchFor); // eslint-disable-line @typescript-eslint/no-loop-func
1121
1142
  if (callable) {
1122
- results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(callable.callable.file.pathAbsolute), callable.callable.functionStatement.range));
1143
+ results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(callable.callable.file.srcPath), callable.callable.functionStatement.range));
1123
1144
  }
1124
1145
  }
1125
1146
  return results;
1126
1147
  }
1127
- let classToken = this.getTokenBefore(token, lexer_1.TokenKind.Class);
1148
+ let classToken = this.parser.getTokenBefore(token, lexer_1.TokenKind.Class);
1128
1149
  if (classToken) {
1129
1150
  let cs = this.parser.references.classStatements.find((cs) => cs.classKeyword.range === classToken.range);
1130
1151
  if (cs === null || cs === void 0 ? void 0 : cs.parentClassName) {
1131
1152
  const nameParts = cs.parentClassName.getNameParts();
1132
1153
  let extendedClass = this.getClassFileLink(nameParts[nameParts.length - 1], nameParts.slice(0, -1).join('.'));
1133
1154
  if (extendedClass) {
1134
- results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(extendedClass.file.pathAbsolute), extendedClass.item.range));
1155
+ results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(extendedClass.file.srcPath), extendedClass.item.range));
1135
1156
  }
1136
1157
  }
1137
1158
  return results;
@@ -1145,23 +1166,21 @@ class BrsFile {
1145
1166
  }
1146
1167
  textToSearchFor = textToSearchFor.substring(startIndex, endIndex);
1147
1168
  }
1148
- //look through local variables first, get the function scope for this position (if it exists)
1149
- const functionScope = this.getFunctionScopeAtPosition(position);
1150
- if (functionScope) {
1151
- //find any variable or label with this name
1152
- for (const varDeclaration of functionScope.variableDeclarations) {
1153
- //we found a variable declaration with this token text!
1154
- if (varDeclaration.name.toLowerCase() === textToSearchFor) {
1155
- const uri = util_1.util.pathToUri(this.pathAbsolute);
1156
- results.push(vscode_languageserver_1.Location.create(uri, varDeclaration.nameRange));
1157
- }
1158
- }
1159
- if (this.tokenFollows(token, lexer_1.TokenKind.Goto)) {
1160
- for (const label of functionScope.labelStatements) {
1161
- if (label.name.toLocaleLowerCase() === textToSearchFor) {
1162
- const uri = util_1.util.pathToUri(this.pathAbsolute);
1163
- results.push(vscode_languageserver_1.Location.create(uri, label.nameRange));
1164
- }
1169
+ const func = this.getFunctionExpressionAtPosition(position);
1170
+ //look through local variables first
1171
+ //find any variable with this name
1172
+ for (const symbol of func.symbolTable.getOwnSymbols()) {
1173
+ //we found a variable declaration with this token text
1174
+ if (symbol.name.toLowerCase() === textToSearchFor) {
1175
+ const uri = util_1.util.pathToUri(this.srcPath);
1176
+ results.push(vscode_languageserver_1.Location.create(uri, symbol.range));
1177
+ }
1178
+ }
1179
+ if (this.parser.tokenFollows(token, lexer_1.TokenKind.Goto)) {
1180
+ for (const label of func.labelStatements) {
1181
+ if (label.tokens.identifier.text.toLocaleLowerCase() === textToSearchFor) {
1182
+ const uri = util_1.util.pathToUri(this.srcPath);
1183
+ results.push(vscode_languageserver_1.Location.create(uri, label.tokens.identifier.range));
1165
1184
  }
1166
1185
  }
1167
1186
  }
@@ -1182,7 +1201,7 @@ class BrsFile {
1182
1201
  }
1183
1202
  const statementHandler = (statement) => {
1184
1203
  if (statement.getName(this.parseMode).toLowerCase() === textToSearchFor) {
1185
- const uri = util_1.util.pathToUri(file.pathAbsolute);
1204
+ const uri = util_1.util.pathToUri(file.srcPath);
1186
1205
  results.push(vscode_languageserver_1.Location.create(uri, statement.range));
1187
1206
  }
1188
1207
  };
@@ -1200,12 +1219,12 @@ class BrsFile {
1200
1219
  //get class fields and members
1201
1220
  const statementHandler = (statement) => {
1202
1221
  if (statement.getName(file.parseMode).toLowerCase() === textToSearchFor) {
1203
- results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(file.pathAbsolute), statement.range));
1222
+ results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(file.srcPath), statement.range));
1204
1223
  }
1205
1224
  };
1206
1225
  const fieldStatementHandler = (statement) => {
1207
1226
  if (statement.name.text.toLowerCase() === textToSearchFor) {
1208
- results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(file.pathAbsolute), statement.range));
1227
+ results.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(file.srcPath), statement.range));
1209
1228
  }
1210
1229
  };
1211
1230
  file.parser.ast.walk((0, visitors_1.createVisitor)({
@@ -1217,8 +1236,9 @@ class BrsFile {
1217
1236
  return results;
1218
1237
  }
1219
1238
  getHover(position) {
1239
+ var _a, _b, _c;
1220
1240
  //get the token at the position
1221
- let token = this.getTokenAt(position);
1241
+ let token = this.parser.getTokenAt(position);
1222
1242
  let hoverTokenTypes = [
1223
1243
  lexer_1.TokenKind.Identifier,
1224
1244
  lexer_1.TokenKind.Function,
@@ -1233,35 +1253,45 @@ class BrsFile {
1233
1253
  let lowerTokenText = token.text.toLowerCase();
1234
1254
  //look through local variables first
1235
1255
  {
1236
- //get the function scope for this position (if exists)
1237
- let functionScope = this.getFunctionScopeAtPosition(position);
1238
- if (functionScope) {
1239
- //find any variable with this name
1240
- for (const varDeclaration of functionScope.variableDeclarations) {
1241
- //we found a variable declaration with this token text!
1242
- if (varDeclaration.name.toLowerCase() === lowerTokenText) {
1243
- let typeText;
1244
- if ((0, reflection_1.isFunctionType)(varDeclaration.type)) {
1245
- typeText = varDeclaration.type.toString();
1246
- }
1247
- else {
1248
- typeText = `${varDeclaration.name} as ${varDeclaration.type.toString()}`;
1249
- }
1256
+ const func = this.getFunctionExpressionAtPosition(position);
1257
+ if (func) {
1258
+ // this identifier could possibly be a class field, so no function expression is available
1259
+ for (const labelStatement of (_a = func === null || func === void 0 ? void 0 : func.labelStatements) !== null && _a !== void 0 ? _a : []) {
1260
+ if (labelStatement.tokens.identifier.text.toLocaleLowerCase() === lowerTokenText) {
1250
1261
  return {
1251
1262
  range: token.range,
1252
- //append the variable name to the front for scope
1253
- contents: typeText
1263
+ contents: `${labelStatement.tokens.identifier.text}: label`
1254
1264
  };
1255
1265
  }
1256
1266
  }
1257
- for (const labelStatement of functionScope.labelStatements) {
1258
- if (labelStatement.name.toLocaleLowerCase() === lowerTokenText) {
1259
- return {
1260
- range: token.range,
1261
- contents: `${labelStatement.name}: label`
1262
- };
1267
+ }
1268
+ const typeTexts = [];
1269
+ for (const scope of this.program.getScopesForFile(this)) {
1270
+ scope.linkSymbolTable();
1271
+ const typeTextPair = this.getSymbolTypeFromToken(token, func, scope);
1272
+ if (typeTextPair) {
1273
+ let scopeTypeText = '';
1274
+ if ((0, reflection_1.isFunctionType)(typeTextPair.type)) {
1275
+ scopeTypeText = (_b = typeTextPair.type) === null || _b === void 0 ? void 0 : _b.toString();
1276
+ }
1277
+ else if (typeTextPair.useExpandedTextOnly) {
1278
+ scopeTypeText = typeTextPair.expandedTokenText;
1279
+ }
1280
+ else {
1281
+ scopeTypeText = `${typeTextPair.expandedTokenText} as ${(_c = typeTextPair.type) === null || _c === void 0 ? void 0 : _c.toString()}`;
1282
+ }
1283
+ if (scopeTypeText && !typeTexts.includes(scopeTypeText)) {
1284
+ typeTexts.push(scopeTypeText);
1263
1285
  }
1264
1286
  }
1287
+ scope.unlinkSymbolTable();
1288
+ }
1289
+ const typeText = typeTexts.join(' | ');
1290
+ if (typeText) {
1291
+ return {
1292
+ range: token.range,
1293
+ contents: typeText
1294
+ };
1265
1295
  }
1266
1296
  }
1267
1297
  //look through all callables in relevant scopes
@@ -1313,10 +1343,10 @@ class BrsFile {
1313
1343
  const func = statement.func;
1314
1344
  const funcStartPosition = func.range.start;
1315
1345
  // Get function comments in reverse order
1316
- let currentToken = this.getTokenAt(funcStartPosition);
1346
+ let currentToken = this.parser.getTokenAt(funcStartPosition);
1317
1347
  let functionComments = [];
1318
1348
  while (currentToken) {
1319
- currentToken = this.getPreviousToken(currentToken);
1349
+ currentToken = this.parser.getPreviousToken(currentToken);
1320
1350
  if (!currentToken) {
1321
1351
  break;
1322
1352
  }
@@ -1360,7 +1390,8 @@ class BrsFile {
1360
1390
  }
1361
1391
  getClassMethod(classStatement, name, walkParents = true) {
1362
1392
  var _a;
1363
- //TODO - would like to write this with getClassHieararchy; but got stuck on working out the scopes to use... :(
1393
+ //TODO - would like to write this with getClassHierarchy; but got stuck on working out the scopes to use... :(
1394
+ //TODO types - this could be solved with symbolTable?
1364
1395
  let statement;
1365
1396
  const statementHandler = (e) => {
1366
1397
  if (!statement && e.name.text.toLowerCase() === name.toLowerCase()) {
@@ -1396,7 +1427,7 @@ class BrsFile {
1396
1427
  return sigHelp;
1397
1428
  }
1398
1429
  getReferences(position) {
1399
- const callSiteToken = this.getTokenAt(position);
1430
+ const callSiteToken = this.parser.getTokenAt(position);
1400
1431
  let locations = [];
1401
1432
  const searchFor = callSiteToken.text.toLowerCase();
1402
1433
  const scopes = this.program.getScopesForFile(this);
@@ -1410,7 +1441,7 @@ class BrsFile {
1410
1441
  file.ast.walk((0, visitors_1.createVisitor)({
1411
1442
  VariableExpression: (e) => {
1412
1443
  if (e.name.text.toLowerCase() === searchFor) {
1413
- locations.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(file.pathAbsolute), e.range));
1444
+ locations.push(vscode_languageserver_1.Location.create(util_1.util.pathToUri(file.srcPath), e.range));
1414
1445
  }
1415
1446
  }
1416
1447
  }), {
@@ -1454,7 +1485,7 @@ class BrsFile {
1454
1485
  getTypedef() {
1455
1486
  const state = new BrsTranspileState_1.BrsTranspileState(this);
1456
1487
  const typedef = this.ast.getTypedef(state);
1457
- const programNode = new source_map_1.SourceNode(null, null, this.pathAbsolute, typedef);
1488
+ const programNode = new source_map_1.SourceNode(null, null, this.srcPath, typedef);
1458
1489
  return programNode.toString();
1459
1490
  }
1460
1491
  dispose() {