@bscotch/gml-parser 1.14.2

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 (208) hide show
  1. package/LICENSE.md +29 -0
  2. package/README.md +151 -0
  3. package/assets/GmlSpec.xml +11419 -0
  4. package/dist/index.d.ts +16 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +13 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/jsdoc.d.ts +79 -0
  9. package/dist/jsdoc.d.ts.map +1 -0
  10. package/dist/jsdoc.feather.d.ts +23 -0
  11. package/dist/jsdoc.feather.d.ts.map +1 -0
  12. package/dist/jsdoc.feather.js +143 -0
  13. package/dist/jsdoc.feather.js.map +1 -0
  14. package/dist/jsdoc.js +468 -0
  15. package/dist/jsdoc.js.map +1 -0
  16. package/dist/jsdoc.test.d.ts +2 -0
  17. package/dist/jsdoc.test.d.ts.map +1 -0
  18. package/dist/jsdoc.test.js +185 -0
  19. package/dist/jsdoc.test.js.map +1 -0
  20. package/dist/lexer.d.ts +3 -0
  21. package/dist/lexer.d.ts.map +1 -0
  22. package/dist/lexer.js +14 -0
  23. package/dist/lexer.js.map +1 -0
  24. package/dist/lexer.test.d.ts +2 -0
  25. package/dist/lexer.test.d.ts.map +1 -0
  26. package/dist/lexer.test.js +78 -0
  27. package/dist/lexer.test.js.map +1 -0
  28. package/dist/lib.objects.d.ts +190 -0
  29. package/dist/lib.objects.d.ts.map +1 -0
  30. package/dist/lib.objects.js +242 -0
  31. package/dist/lib.objects.js.map +1 -0
  32. package/dist/logger.d.ts +13 -0
  33. package/dist/logger.d.ts.map +1 -0
  34. package/dist/logger.js +14 -0
  35. package/dist/logger.js.map +1 -0
  36. package/dist/modules.d.ts +19 -0
  37. package/dist/modules.d.ts.map +1 -0
  38. package/dist/modules.js +320 -0
  39. package/dist/modules.js.map +1 -0
  40. package/dist/modules.test.d.ts +2 -0
  41. package/dist/modules.test.d.ts.map +1 -0
  42. package/dist/modules.test.js +57 -0
  43. package/dist/modules.test.js.map +1 -0
  44. package/dist/modules.types.d.ts +78 -0
  45. package/dist/modules.types.d.ts.map +1 -0
  46. package/dist/modules.types.js +2 -0
  47. package/dist/modules.types.js.map +1 -0
  48. package/dist/modules.util.d.ts +5 -0
  49. package/dist/modules.util.d.ts.map +1 -0
  50. package/dist/modules.util.js +13 -0
  51. package/dist/modules.util.js.map +1 -0
  52. package/dist/parser.d.ts +121 -0
  53. package/dist/parser.d.ts.map +1 -0
  54. package/dist/parser.js +571 -0
  55. package/dist/parser.js.map +1 -0
  56. package/dist/parser.test.d.ts +2 -0
  57. package/dist/parser.test.d.ts.map +1 -0
  58. package/dist/parser.test.js +143 -0
  59. package/dist/parser.test.js.map +1 -0
  60. package/dist/parser.utility.d.ts +29 -0
  61. package/dist/parser.utility.d.ts.map +1 -0
  62. package/dist/parser.utility.js +125 -0
  63. package/dist/parser.utility.js.map +1 -0
  64. package/dist/project.asset.d.ts +115 -0
  65. package/dist/project.asset.d.ts.map +1 -0
  66. package/dist/project.asset.js +802 -0
  67. package/dist/project.asset.js.map +1 -0
  68. package/dist/project.code.d.ts +130 -0
  69. package/dist/project.code.d.ts.map +1 -0
  70. package/dist/project.code.js +570 -0
  71. package/dist/project.code.js.map +1 -0
  72. package/dist/project.d.ts +533 -0
  73. package/dist/project.d.ts.map +1 -0
  74. package/dist/project.diagnostics.d.ts +32 -0
  75. package/dist/project.diagnostics.d.ts.map +1 -0
  76. package/dist/project.diagnostics.js +23 -0
  77. package/dist/project.diagnostics.js.map +1 -0
  78. package/dist/project.js +1216 -0
  79. package/dist/project.js.map +1 -0
  80. package/dist/project.location.d.ts +133 -0
  81. package/dist/project.location.d.ts.map +1 -0
  82. package/dist/project.location.js +219 -0
  83. package/dist/project.location.js.map +1 -0
  84. package/dist/project.native.d.ts +26 -0
  85. package/dist/project.native.d.ts.map +1 -0
  86. package/dist/project.native.js +321 -0
  87. package/dist/project.native.js.map +1 -0
  88. package/dist/project.spec.d.ts +1298 -0
  89. package/dist/project.spec.d.ts.map +1 -0
  90. package/dist/project.spec.js +263 -0
  91. package/dist/project.spec.js.map +1 -0
  92. package/dist/project.test.d.ts +2 -0
  93. package/dist/project.test.d.ts.map +1 -0
  94. package/dist/project.test.js +633 -0
  95. package/dist/project.test.js.map +1 -0
  96. package/dist/shaderDefaults.d.ts +3 -0
  97. package/dist/shaderDefaults.d.ts.map +1 -0
  98. package/dist/shaderDefaults.js +32 -0
  99. package/dist/shaderDefaults.js.map +1 -0
  100. package/dist/signifiers.d.ts +54 -0
  101. package/dist/signifiers.d.ts.map +1 -0
  102. package/dist/signifiers.flags.d.ts +38 -0
  103. package/dist/signifiers.flags.d.ts.map +1 -0
  104. package/dist/signifiers.flags.js +131 -0
  105. package/dist/signifiers.flags.js.map +1 -0
  106. package/dist/signifiers.js +117 -0
  107. package/dist/signifiers.js.map +1 -0
  108. package/dist/spine.d.ts +28 -0
  109. package/dist/spine.d.ts.map +1 -0
  110. package/dist/spine.js +64 -0
  111. package/dist/spine.js.map +1 -0
  112. package/dist/spine.test.d.ts +2 -0
  113. package/dist/spine.test.d.ts.map +1 -0
  114. package/dist/spine.test.js +420 -0
  115. package/dist/spine.test.js.map +1 -0
  116. package/dist/spine.types.d.ts +89 -0
  117. package/dist/spine.types.d.ts.map +1 -0
  118. package/dist/spine.types.js +2 -0
  119. package/dist/spine.types.js.map +1 -0
  120. package/dist/test.lib.d.ts +3 -0
  121. package/dist/test.lib.d.ts.map +1 -0
  122. package/dist/test.lib.js +16 -0
  123. package/dist/test.lib.js.map +1 -0
  124. package/dist/tokens.categories.d.ts +22 -0
  125. package/dist/tokens.categories.d.ts.map +1 -0
  126. package/dist/tokens.categories.js +78 -0
  127. package/dist/tokens.categories.js.map +1 -0
  128. package/dist/tokens.code.d.ts +2 -0
  129. package/dist/tokens.code.d.ts.map +1 -0
  130. package/dist/tokens.code.js +523 -0
  131. package/dist/tokens.code.js.map +1 -0
  132. package/dist/tokens.d.ts +130 -0
  133. package/dist/tokens.d.ts.map +1 -0
  134. package/dist/tokens.js +13 -0
  135. package/dist/tokens.js.map +1 -0
  136. package/dist/tokens.lib.d.ts +15 -0
  137. package/dist/tokens.lib.d.ts.map +1 -0
  138. package/dist/tokens.lib.js +12 -0
  139. package/dist/tokens.lib.js.map +1 -0
  140. package/dist/tokens.shared.d.ts +4 -0
  141. package/dist/tokens.shared.d.ts.map +1 -0
  142. package/dist/tokens.shared.js +35 -0
  143. package/dist/tokens.shared.js.map +1 -0
  144. package/dist/tokens.strings.d.ts +5 -0
  145. package/dist/tokens.strings.d.ts.map +1 -0
  146. package/dist/tokens.strings.js +111 -0
  147. package/dist/tokens.strings.js.map +1 -0
  148. package/dist/types.checks.d.ts +50 -0
  149. package/dist/types.checks.d.ts.map +1 -0
  150. package/dist/types.checks.js +246 -0
  151. package/dist/types.checks.js.map +1 -0
  152. package/dist/types.d.ts +152 -0
  153. package/dist/types.d.ts.map +1 -0
  154. package/dist/types.feather.d.ts +21 -0
  155. package/dist/types.feather.d.ts.map +1 -0
  156. package/dist/types.feather.js +156 -0
  157. package/dist/types.feather.js.map +1 -0
  158. package/dist/types.hover.d.ts +4 -0
  159. package/dist/types.hover.d.ts.map +1 -0
  160. package/dist/types.hover.js +99 -0
  161. package/dist/types.hover.js.map +1 -0
  162. package/dist/types.js +457 -0
  163. package/dist/types.js.map +1 -0
  164. package/dist/types.primitives.d.ts +10 -0
  165. package/dist/types.primitives.d.ts.map +1 -0
  166. package/dist/types.primitives.js +88 -0
  167. package/dist/types.primitives.js.map +1 -0
  168. package/dist/types.sprites.d.ts +8 -0
  169. package/dist/types.sprites.d.ts.map +1 -0
  170. package/dist/types.sprites.js +417 -0
  171. package/dist/types.sprites.js.map +1 -0
  172. package/dist/types.test.d.ts +2 -0
  173. package/dist/types.test.d.ts.map +1 -0
  174. package/dist/types.test.js +62 -0
  175. package/dist/types.test.js.map +1 -0
  176. package/dist/util.d.ts +50 -0
  177. package/dist/util.d.ts.map +1 -0
  178. package/dist/util.js +168 -0
  179. package/dist/util.js.map +1 -0
  180. package/dist/util.test.d.ts +3 -0
  181. package/dist/util.test.d.ts.map +1 -0
  182. package/dist/util.test.js +63 -0
  183. package/dist/util.test.js.map +1 -0
  184. package/dist/visitor.assign.d.ts +24 -0
  185. package/dist/visitor.assign.d.ts.map +1 -0
  186. package/dist/visitor.assign.js +112 -0
  187. package/dist/visitor.assign.js.map +1 -0
  188. package/dist/visitor.d.ts +89 -0
  189. package/dist/visitor.d.ts.map +1 -0
  190. package/dist/visitor.functionExpression.d.ts +7 -0
  191. package/dist/visitor.functionExpression.d.ts.map +1 -0
  192. package/dist/visitor.functionExpression.js +216 -0
  193. package/dist/visitor.functionExpression.js.map +1 -0
  194. package/dist/visitor.globals.d.ts +59 -0
  195. package/dist/visitor.globals.d.ts.map +1 -0
  196. package/dist/visitor.globals.js +271 -0
  197. package/dist/visitor.globals.js.map +1 -0
  198. package/dist/visitor.identifierAccessor.d.ts +6 -0
  199. package/dist/visitor.identifierAccessor.d.ts.map +1 -0
  200. package/dist/visitor.identifierAccessor.js +381 -0
  201. package/dist/visitor.identifierAccessor.js.map +1 -0
  202. package/dist/visitor.js +605 -0
  203. package/dist/visitor.js.map +1 -0
  204. package/dist/visitor.processor.d.ts +66 -0
  205. package/dist/visitor.processor.d.ts.map +1 -0
  206. package/dist/visitor.processor.js +147 -0
  207. package/dist/visitor.processor.js.map +1 -0
  208. package/package.json +63 -0
@@ -0,0 +1,633 @@
1
+ import { expect } from 'chai';
2
+ import { logger } from './logger.js';
3
+ import { Project } from './project.js';
4
+ import { Native } from './project.native.js';
5
+ import { Signifier } from './signifiers.js';
6
+ import { resetSandbox } from './test.lib.js';
7
+ import { Type } from './types.js';
8
+ import { assert, ok } from './util.js';
9
+ describe('Project', function () {
10
+ it('can load the GML spec', async function () {
11
+ const spec = await Native.from(undefined, new Type('Struct'), new Map());
12
+ expect(spec).to.exist;
13
+ // STRUCTS AND CONSTS
14
+ const track = spec.types.get('Struct.Track');
15
+ ok(track);
16
+ ok(track.kind === 'Struct');
17
+ const name = track.getMember('name');
18
+ ok(name);
19
+ expect(name.type.kind).to.equal('String');
20
+ const visible = track.getMember('visible');
21
+ ok(visible);
22
+ expect(visible.type.kind).to.equal('Bool');
23
+ const tracks = track.getMember('tracks');
24
+ ok(tracks);
25
+ expect(tracks.type.kind).to.equal('Array');
26
+ expect(tracks.type.items[0].kind).to.equal('Struct');
27
+ expect(tracks.type.items[0].type[0]).to.eql(track);
28
+ const keyframes = track.getMember('keyframes');
29
+ ok(keyframes);
30
+ expect(keyframes.type.kind).to.equal('Array');
31
+ expect(keyframes.type.items[0].kind).to.equal('Struct');
32
+ expect(keyframes.type.items[0].type[0]).to.eql(spec.types.get('Struct.Keyframe'));
33
+ const typeField = track.getMember('type');
34
+ ok(typeField);
35
+ const expectedTypeType = spec.types.get('Constant.SequenceTrackType');
36
+ ok(expectedTypeType);
37
+ ok(typeField.type.type[0] === expectedTypeType);
38
+ ok(expectedTypeType.kind === 'Real');
39
+ // VARIABLES
40
+ const depthSymbol = spec.globalSelf.getMember('depth');
41
+ ok(depthSymbol);
42
+ expect(depthSymbol.type.kind).to.equal('Real');
43
+ // FUNCTIONS
44
+ const scriptExecuteType = spec.types.get('Function.script_execute');
45
+ const scriptExecuteSymbol = spec.globalSelf.getMember('script_execute');
46
+ ok(scriptExecuteSymbol);
47
+ ok(scriptExecuteSymbol.type.type[0] === scriptExecuteType);
48
+ ok(scriptExecuteType);
49
+ ok(scriptExecuteType.kind === 'Function');
50
+ expect(scriptExecuteType.listParameters()).to.have.lengthOf(2);
51
+ expect(scriptExecuteType.listParameters()[0].name).to.equal('scr');
52
+ expect(scriptExecuteType.listParameters()[0].type.type).to.have.lengthOf(3);
53
+ expect(scriptExecuteType.listParameters()[0].type.type[0].kind).to.equal('String');
54
+ expect(scriptExecuteType.listParameters()[0].type.type[1].kind).to.equal('Function');
55
+ expect(scriptExecuteType.listParameters()[0].type.type[2].kind).to.equal('Asset.GMScript');
56
+ expect(scriptExecuteType.listParameters()[1].name).to.equal('...');
57
+ });
58
+ it('can use fallback GmlSpec', async function () {
59
+ await Project.fallbackGmlSpecPath.exists({ assert: true });
60
+ });
61
+ it('can analyze a representative project', async function () {
62
+ const projectDir = 'samples/project';
63
+ const project = await Project.initialize(projectDir);
64
+ ok(project);
65
+ //#region ASSETS
66
+ const script = project.getAssetByName('Script1');
67
+ const scriptFile = script.gmlFile;
68
+ const jsdocs = project.getAssetByName('Jsdocs');
69
+ const jsdocsFile = jsdocs.gmlFile;
70
+ const complexScript = project.getAssetByName('Complicated');
71
+ const complexScriptFile = complexScript.gmlFile;
72
+ const recoveryScript = project.getAssetByName('Recovery');
73
+ const recoveryScriptFile = recoveryScript.gmlFile;
74
+ const obj = project.getAssetByName('o_object');
75
+ const objCreate = obj.gmlFilesArray.find((f) => f.name === 'Create_0');
76
+ const objStep = obj.gmlFilesArray.find((f) => f.name === 'Step_0');
77
+ const parent = project.getAssetByName('o_parent');
78
+ const child = project.getAssetByName('o_child1');
79
+ const grandchild = project.getAssetByName('o_child1_child');
80
+ const child2 = project.getAssetByName('o_child2');
81
+ ok(script);
82
+ ok(scriptFile);
83
+ ok(jsdocs);
84
+ ok(jsdocsFile);
85
+ ok(complexScript);
86
+ ok(complexScriptFile);
87
+ ok(obj);
88
+ ok(objCreate);
89
+ ok(objStep);
90
+ ok(recoveryScript);
91
+ ok(recoveryScriptFile);
92
+ ok(parent);
93
+ ok(child);
94
+ ok(grandchild);
95
+ ok(child2);
96
+ //#endregion ASSETS
97
+ //#region OBJECT INHERITANCE
98
+ // Child1 and Child2 both inherit from Parent
99
+ // Child1Child inherits from Child1
100
+ // Child2 does not call event_inherited in its Create event
101
+ const parentVars = ['parent_var'];
102
+ const childVars = ['child1_var'];
103
+ const grandchildVars = ['child1_child_var'];
104
+ const child2Vars = ['child2_var'];
105
+ // Check inheritance links
106
+ ok(child.parent === parent);
107
+ ok(grandchild.parent === child);
108
+ ok(child2.parent === parent);
109
+ // Check that variables are propery inherited
110
+ expect(parent.instanceType?.listMembers().map((m) => m.name)).to.include.members(parentVars);
111
+ expect(child.instanceType?.listMembers().map((m) => m.name)).to.include.members([...parentVars, ...childVars]);
112
+ expect(grandchild.instanceType?.listMembers().map((m) => m.name)).to.include.members([...parentVars, ...childVars, ...grandchildVars]);
113
+ expect(child2.instanceType?.listMembers().map((m) => m.name)).to.include.members(child2Vars);
114
+ expect(child2.instanceType?.listMembers().map((m) => m.name)).not.to.include.members(parentVars);
115
+ // Check that a reference to the parent_var works in the grandchild
116
+ const grandChildRef = grandchild.gmlFile.getReferenceAt(4, 24);
117
+ ok(grandChildRef);
118
+ expect(grandChildRef.item.name).to.equal('parent_var');
119
+ ok(grandChildRef.item === parent.instanceType.getMember('parent_var'));
120
+ ok(grandChildRef.item.def);
121
+ //#endregion OBJECT INHERITANCE
122
+ // Check fancy assignmnent operators
123
+ const decrementer = script.gmlFile.getReferenceAt(72, 3);
124
+ ok(decrementer);
125
+ const decrementRhs = script.gmlFile.getReferenceAt(72, 19);
126
+ ok(decrementRhs);
127
+ // Check assignment to an unknown variable
128
+ const unknownVar = script.gmlFile.getReferenceAt(76, 13);
129
+ ok(!unknownVar);
130
+ const unknownVarRhs = script.gmlFile.getReferenceAt(76, 24);
131
+ ok(unknownVarRhs);
132
+ // Check reference inside of an accessor
133
+ const accessorRef = script.gmlFile.getReferenceAt(81, 27);
134
+ ok(accessorRef);
135
+ //#region GLOBALVARS
136
+ const globalVarName = 'GLOBAL_SCRIPT_VAR';
137
+ const globalvarDef = scriptFile.getReferenceAt(2, 18);
138
+ const globalvarRef = scriptFile.getReferenceAt(2, 37);
139
+ const otherGlobalvarRef = objStep.getReferenceAt(2, 36);
140
+ ok(globalvarDef);
141
+ ok(globalvarRef);
142
+ ok(otherGlobalvarRef);
143
+ // All refs should point to the same thing
144
+ ok(globalvarDef.item === globalvarRef.item);
145
+ ok(globalvarDef.item === otherGlobalvarRef.item);
146
+ // The definition should exist and be named
147
+ const item = globalvarDef.item;
148
+ ok(item.isRenameable);
149
+ ok(item.def);
150
+ ok(item.name === globalVarName);
151
+ // The globalvar should have appropriate symbol and type info
152
+ ok(item.$tag === 'Sym');
153
+ ok(item.global === true);
154
+ //#endregion GLOBALVARS
155
+ //#region REF RENAMING
156
+ expect(globalvarRef.text).to.equal(globalVarName);
157
+ expect(globalvarRef.isRenameable).to.equal(true);
158
+ //#endregion REF RENAMING
159
+ //#region ROOT SCRIPT SCOPE
160
+ const inRootScriptScope = scriptFile.getInScopeSymbolsAt(762);
161
+ ok(inRootScriptScope.length);
162
+ // Local scope
163
+ const localConstructed = inRootScriptScope.find((id) => id.name === 'const');
164
+ ok(localConstructed);
165
+ ok(localConstructed.local);
166
+ ok(!localConstructed.global);
167
+ // Global scope
168
+ const globalConstructor = inRootScriptScope.find((id) => id.name === 'GlobalConstructor');
169
+ ok(globalConstructor);
170
+ ok(!globalConstructor.local);
171
+ ok(globalConstructor.global);
172
+ ok(globalConstructor.name === 'GlobalConstructor');
173
+ ok(globalConstructor.type.constructs);
174
+ ok(globalConstructor.type.constructs[0].name === 'GlobalConstructor');
175
+ ok(globalConstructor.type.type[0].isFunction);
176
+ expect(globalConstructor.type.kind).to.equal('Function');
177
+ expect(globalConstructor.type.type[0].isConstructor).to.equal(true);
178
+ // Instance scope (should not be found)
179
+ ok(!inRootScriptScope.find((id) => id.name === 'instance_function'));
180
+ // Deeper local scope (should not be found)
181
+ ok(!inRootScriptScope.find((id) => id.name === '_name'));
182
+ //#endregion ROOT SCRIPT SCOPE
183
+ //#region FUNCTION SCOPE
184
+ // Params
185
+ const paramName = '_name';
186
+ const paramRef = scriptFile.getReferenceAt({ line: 18, column: 32 });
187
+ ok(paramRef);
188
+ expect(paramRef.text).to.equal(paramName);
189
+ const param = paramRef.item;
190
+ ok(param);
191
+ ok(param.local);
192
+ ok(param.parameter);
193
+ expect(param.name).to.equal(paramName);
194
+ // Params should be visible in the function scope
195
+ const inFunctionScope = scriptFile.getInScopeSymbolsAt(19, 16);
196
+ ok(inFunctionScope.length);
197
+ ok(inFunctionScope.find((id) => id.name === paramName));
198
+ // And so should local vars
199
+ const inFunctionLocalvar = inFunctionScope.find((id) => id.name === 'local');
200
+ ok(inFunctionLocalvar);
201
+ ok(scriptFile.getReferenceAt(21, 10).item === inFunctionLocalvar);
202
+ //#endregion FUNCTION SCOPE
203
+ //#region INSTANCE SCOPE
204
+ const instanceVarName = 'instance_variable';
205
+ const inInstanceScope = objStep.getReferenceAt(4, 9);
206
+ const objectType = obj.instanceType;
207
+ ok(objectType);
208
+ ok(inInstanceScope);
209
+ expect(inInstanceScope.text).to.equal(instanceVarName);
210
+ ok(inInstanceScope.item.name === instanceVarName);
211
+ ok(inInstanceScope.item.instance);
212
+ // Are functions properly added to self?
213
+ const instanceFunctionName = 'instance_function';
214
+ const instanceFunction = objectType.getMember(instanceFunctionName);
215
+ ok(instanceFunction);
216
+ //#endregion INSTANCE SCOPE
217
+ //#region ENUMS
218
+ const enumName = 'SurpriseEnum';
219
+ const enumMemberName = 'another_surprise';
220
+ const enumDef = scriptFile.getReferenceAt(22, 12);
221
+ const enumMemberDef = scriptFile.getReferenceAt(22, 40);
222
+ ok(enumDef);
223
+ expect(enumDef.text).to.equal(enumName);
224
+ ok(enumDef.item.name === enumName);
225
+ ok(enumMemberDef);
226
+ ok(enumMemberDef.item.name === enumMemberName);
227
+ const enumRef = objCreate.getReferenceAt(3, 23);
228
+ const enumMemberRef = objCreate.getReferenceAt(3, 38);
229
+ ok(enumRef);
230
+ ok(enumRef.item.name === enumName);
231
+ ok(enumMemberRef);
232
+ expect(enumMemberRef.text).to.equal(enumMemberName);
233
+ ok(enumMemberRef.item.name === enumMemberName);
234
+ //#endregion ENUMS
235
+ //#region RECOVERY
236
+ const enumAutocompleteScope = recoveryScriptFile.getScopeRangeAt(3, 22);
237
+ ok(enumAutocompleteScope);
238
+ const enumAutocompleteList = recoveryScriptFile.getInScopeSymbolsAt(3, 22);
239
+ ok(enumAutocompleteList);
240
+ expect(enumAutocompleteList.length).to.equal(2);
241
+ //#endregion RECOVERY
242
+ //#region CONSTRUCTORS
243
+ const constructorName = 'GlobalConstructor';
244
+ const constructorDef = scriptFile.getReferenceAt(18, 17);
245
+ const constructorSymbol = constructorDef.item;
246
+ const constructorType = constructorSymbol.type;
247
+ ok(constructorDef);
248
+ expect(constructorDef.text).to.equal(constructorName);
249
+ ok(constructorSymbol);
250
+ ok(constructorType);
251
+ ok(constructorSymbol.name === constructorName);
252
+ ok(constructorSymbol instanceof Signifier);
253
+ expect(constructorSymbol.type.kind).to.equal('Function');
254
+ expect(constructorSymbol.type.type[0].isConstructor).to.equal(true);
255
+ expect(constructorType.type[0].name).to.equal(constructorName);
256
+ expect(constructorType.type[0].listParameters()).to.have.lengthOf(2);
257
+ expect(constructorType.constructs[0].kind).to.equal('Struct');
258
+ expect(constructorType.constructs[0].name).to.equal(constructorName);
259
+ ok(project.self.getMember(constructorName) === constructorSymbol);
260
+ ok(project.types.get(`Struct.${constructorName}`) ===
261
+ constructorType.constructs[0]);
262
+ //#endregion CONSTRUCTORS
263
+ //#region FUNCTION CALLS
264
+ ok(!scriptFile.getFunctionArgRangeAt(29, 35).hasExpression);
265
+ ok(scriptFile.getFunctionArgRangeAt(41, 50).hasExpression);
266
+ //#endregion FUNCTION CALLS
267
+ //#region DOT ASSIGNMENTS
268
+ const dotAssignedRefName = 'another_instance_variable';
269
+ const dotAssignedRef = objCreate.getReferenceAt(20, 14);
270
+ const dotAssignedType = dotAssignedRef?.item;
271
+ ok(dotAssignedRef);
272
+ expect(dotAssignedRef.text).to.equal(dotAssignedRefName);
273
+ ok(dotAssignedRef.item.name === dotAssignedRefName);
274
+ ok(dotAssignedType);
275
+ ok(dotAssignedType.parent === obj.instanceType?.extends);
276
+ //#endregion DOT ASSIGNMENTS
277
+ //#region FUNCTIONS
278
+ // Check the return type of a function
279
+ const functionDefRef = complexScriptFile.getReferenceAt(119, 22);
280
+ expect((functionDefRef?.item).type.returns[0].kind).to.equal('Array');
281
+ const globalFunction = scriptFile.getReferenceAt(6, 19);
282
+ ok(globalFunction);
283
+ ok(globalFunction.item.name === 'global_function');
284
+ //#endregion FUNCTIONS
285
+ await validateCrossFileDiagnostics(project);
286
+ validateGenerics(project);
287
+ validateWithContexts(project);
288
+ validateFunctionContexts(project);
289
+ validateJsdocs(project);
290
+ validateBschemaConstructor(project);
291
+ await validateAccessorTypes(project);
292
+ // Reprocess a file and ensure that the tests still pass
293
+ await complexScriptFile.reload();
294
+ logger.log('Re-running after reload...');
295
+ validateBschemaConstructor(project);
296
+ });
297
+ it('can sync datafiles', async function () {
298
+ const project = await resetSandbox();
299
+ await project.dir
300
+ .join('datafiles/test-folder/test-file.txt')
301
+ .write('hello');
302
+ await project.syncIncludedFiles();
303
+ assert(project.datafiles.find((f) => f.name === 'test-file.txt' && f.filePath === 'datafiles/test-folder'));
304
+ });
305
+ xit('can parse sample project', async function () {
306
+ const projectDir = process.env.GML_PARSER_SAMPLE_PROJECT_DIR;
307
+ ok(projectDir, 'A dotenv file should provide a path to a full sample project, as env var GML_PARSER_SAMPLE_PROJECT_DIR');
308
+ const project = await Project.initialize(projectDir);
309
+ });
310
+ });
311
+ function validateGenerics(project) {
312
+ const scriptFile = project.getAssetByName('Generics').gmlFile;
313
+ const o_object = project.getAssetByName('o_object');
314
+ // SIMPLE IDENTIFY FUNCTION
315
+ const identifyFunc = scriptFile.getReferenceAt(4, 15).item;
316
+ const returns = identifyFunc.type.returns;
317
+ expect(returns).to.have.lengthOf(1);
318
+ expect(returns[0].type[0].name).to.equal('T');
319
+ const sampleType = scriptFile.getReferenceAt(8, 9).item.type.type[0];
320
+ const returnedSample = scriptFile.getReferenceAt(11, 11).item;
321
+ const returnedSampleType = returnedSample.type.type[0];
322
+ ok(sampleType.kind === returnedSampleType.kind);
323
+ // InstanceType<>
324
+ const returnedInstance = scriptFile.getReferenceAt(24, 7).item.type.type[0];
325
+ ok(returnedInstance.kind === o_object.instanceType.kind);
326
+ // ObjectType<>
327
+ const returnedObject = scriptFile.getReferenceAt(25, 7).item.type.type[0];
328
+ ok(returnedObject.kind === o_object.assetType.kind);
329
+ }
330
+ async function validateCrossFileDiagnostics(project) {
331
+ // In VSCode, we were finding that when a Create event was updated
332
+ // then a struct stored in its variables would have incorrect "missing"
333
+ // properties in the Step of the same object.
334
+ const asset = project.getAssetByName('o_child1_child');
335
+ const createEvent = asset.getEventByName('Create_0');
336
+ const stepEvent = asset.getEventByName('Step_0');
337
+ const propName = 'idle';
338
+ const declaration = createEvent.getReferenceAt(5, 4);
339
+ const reference = stepEvent.getReferenceAt(1, 20);
340
+ const propertyDeclaration = createEvent.getReferenceAt(6, 5);
341
+ const propertyReference = stepEvent.getReferenceAt(1, 25);
342
+ assert(declaration.item === reference.item);
343
+ assert(propertyDeclaration.item === propertyReference.item);
344
+ assert(propertyDeclaration.item.name === propName);
345
+ assert(declaration.item.type.type[0].getMember(propName) ===
346
+ propertyDeclaration.item);
347
+ // Should not have any diagnostics in these files.
348
+ const stepDiagnostics = stepEvent.getDiagnostics().UNDECLARED_VARIABLE_REFERENCE;
349
+ expect(stepDiagnostics).to.have.lengthOf(0);
350
+ // Upon reloading the Create event, should STILL have
351
+ // no diagnostics and the references
352
+ // should all go to the same, *defined* signifiers.
353
+ await createEvent.reload(createEvent.content, { reloadDirty: true });
354
+ const reloadedDeclaration = createEvent.getReferenceAt(5, 4);
355
+ assert(reloadedDeclaration.item === declaration.item);
356
+ const reloadedReference = stepEvent.getReferenceAt(1, 20);
357
+ expect(reloadedReference.item).to.equal(reloadedDeclaration.item);
358
+ const reloadedPropertyDeclaration = createEvent.getReferenceAt(6, 5);
359
+ assert(reloadedPropertyDeclaration.item.name === propName);
360
+ assert(reloadedPropertyDeclaration.item === propertyDeclaration.item);
361
+ const reloadedPropertyReference = stepEvent.getReferenceAt(1, 25);
362
+ assert(reloadedPropertyReference.item === reloadedPropertyDeclaration.item);
363
+ const reloadedStepDiagnostics = stepEvent.getDiagnostics().UNDECLARED_VARIABLE_REFERENCE;
364
+ expect(reloadedStepDiagnostics).to.have.lengthOf(0);
365
+ }
366
+ async function validateAccessorTypes(project) {
367
+ // There was an issue where the type retrieved from an accessor
368
+ // would end up being a *new* type, causing structs to lose
369
+ // properties upon reload
370
+ // The sample case is in the `Reactions` script, where the
371
+ // `_timer` localvar initially has the expected struct type
372
+ // but after reloading it is missing most of its properties
373
+ const reactionTimerFunction = project.self.getMember('ReactionTimer');
374
+ ok(reactionTimerFunction);
375
+ const reactionTimerConstruct = reactionTimerFunction.type.constructs[0];
376
+ ok(reactionTimerConstruct);
377
+ ok(reactionTimerConstruct.name === 'ReactionTimer');
378
+ const expectedMemberNames = [
379
+ 'reaction_id',
380
+ 'timer',
381
+ 'maxtime_min',
382
+ 'maxtime_max',
383
+ 'maxtime',
384
+ 'event_id',
385
+ ];
386
+ const assertAllMembersExist = (type) => {
387
+ expectedMemberNames.forEach((name) => {
388
+ ok(type.type[0].getMember(name), `Missing member ${name}`);
389
+ });
390
+ };
391
+ const reactionsAsset = project.getAssetByName('Reactions');
392
+ const reactionsFile = reactionsAsset.gmlFile;
393
+ const functionScope = reactionsFile.getScopeRangeAt(3, 1);
394
+ ok(functionScope);
395
+ const timerVar = functionScope.local.getMember('_timer');
396
+ ok(timerVar);
397
+ ok(timerVar.type.type[0] === reactionTimerConstruct, 'Timer var type is not the expected type');
398
+ let withContext = reactionsFile.getScopeRangeAt(5, 1);
399
+ ok(withContext.self === reactionTimerConstruct);
400
+ assertAllMembersExist(timerVar.type);
401
+ // Reload the file and ensure that the type is still correct
402
+ await reactionsFile.reload();
403
+ const reloadedTimerVar = reactionsFile
404
+ .getScopeRangeAt(3, 1)
405
+ .local.getMember('_timer');
406
+ ok(reloadedTimerVar);
407
+ ok(reloadedTimerVar.type.type[0] === reactionTimerConstruct, 'Timer var type is not the expected type after reload');
408
+ withContext = reactionsFile.getScopeRangeAt(5, 1);
409
+ ok(withContext.self === reactionTimerConstruct);
410
+ assertAllMembersExist(reloadedTimerVar.type);
411
+ }
412
+ function validateFunctionContexts(project) {
413
+ const complicatedScriptFile = project.getAssetByName('Complicated').gmlFile;
414
+ const functionScript = project.getAssetByName('FunctionSelf');
415
+ const functionScriptFile = functionScript.gmlFile;
416
+ ok(functionScript);
417
+ ok(functionScriptFile);
418
+ ok(complicatedScriptFile);
419
+ // GLOBAL CONSTRUCTED CONTEXT
420
+ const bschemaGlobalContext = complicatedScriptFile.getReferenceAt(7, 14).item;
421
+ const functionWithBschemaGlobalContext = functionScriptFile.getScopeRangeAt(2, 32);
422
+ ok(functionWithBschemaGlobalContext &&
423
+ functionWithBschemaGlobalContext.self ===
424
+ bschemaGlobalContext.type.constructs[0]);
425
+ // OBJECT CONTEXT
426
+ const obj = project.getAssetByName('o_object');
427
+ ok(obj && obj.instanceType);
428
+ const functionWithObjectContext = functionScriptFile.getScopeRangeAt(7, 25);
429
+ ok(functionWithObjectContext &&
430
+ functionWithObjectContext.self === obj.assetType);
431
+ // INSTANCE CONTEXT
432
+ const functionWithInstanceContext = functionScriptFile.getScopeRangeAt(12, 27);
433
+ ok(functionWithInstanceContext &&
434
+ functionWithInstanceContext.self === obj.instanceType);
435
+ }
436
+ function validateJsdocs(project) {
437
+ const jsdocsFile = project.getAssetByName('Jsdocs').gmlFile;
438
+ const jsdocs = jsdocsFile.jsdocs;
439
+ expect(jsdocs).to.have.lengthOf(8);
440
+ // Check positions
441
+ let jsdoc = jsdocs[0];
442
+ expect(jsdoc.start.line).to.equal(1);
443
+ expect(jsdoc.start.column).to.equal(1);
444
+ expect(jsdoc.end.line).to.equal(11);
445
+ expect(jsdoc.end.column).to.equal(16);
446
+ expect(jsdoc.params[2].optional).to.equal(true);
447
+ expect(jsdoc.params[2].name.content).to.equal('maybe');
448
+ expect(jsdoc.params[2].name.start.line).to.equal(6);
449
+ expect(jsdoc.params[2].name.end.line).to.equal(6);
450
+ expect(jsdoc.params[2].name.start.column).to.equal(22);
451
+ expect(jsdoc.params[2].name.end.column).to.equal(26);
452
+ expect(jsdoc.params[2].type.content).to.equal('Struct');
453
+ expect(jsdoc.params[2].type.start.line).to.equal(6);
454
+ expect(jsdoc.params[2].type.end.line).to.equal(6);
455
+ expect(jsdoc.params[2].type.start.column).to.equal(13);
456
+ expect(jsdoc.params[2].type.end.column).to.equal(18);
457
+ jsdoc = jsdocs[1];
458
+ expect(jsdoc.start.line).to.equal(13);
459
+ expect(jsdoc.start.column).to.equal(3);
460
+ expect(jsdoc.end.line).to.equal(14);
461
+ expect(jsdoc.end.column).to.equal(21);
462
+ jsdoc = jsdocs[2];
463
+ expect(jsdoc.start.line).to.equal(17);
464
+ expect(jsdoc.start.column).to.equal(3);
465
+ expect(jsdoc.end.line).to.equal(19);
466
+ expect(jsdoc.end.column).to.equal(6);
467
+ jsdoc = jsdocs[3];
468
+ expect(jsdoc.start.line).to.equal(23);
469
+ expect(jsdoc.start.column).to.equal(1);
470
+ expect(jsdoc.end.line).to.equal(34);
471
+ expect(jsdoc.end.column).to.equal(38);
472
+ // This one was misbehaving despite the above tests passing
473
+ const scriptFile = project.getAssetByName('Script1').gmlFile;
474
+ const lastJsdoc = scriptFile.jsdocs.at(-3);
475
+ expect(lastJsdoc.start.line).to.equal(55);
476
+ expect(lastJsdoc.start.column).to.equal(1);
477
+ expect(lastJsdoc.end.line).to.equal(55);
478
+ expect(lastJsdoc.end.column).to.equal(42);
479
+ expect(lastJsdoc.params[0].name.content).to.equal('hello');
480
+ expect(lastJsdoc.params[0].type.start.line).to.equal(55);
481
+ expect(lastJsdoc.params[0].type.end.line).to.equal(55);
482
+ expect(lastJsdoc.params[0].type.start.column).to.equal(11);
483
+ expect(lastJsdoc.params[0].type.end.column).to.equal(34);
484
+ // Make sure that a JSDoc description without a type annotation
485
+ // doesn't cause the type to be forced to `Any`
486
+ const undescribedVar = jsdocsFile.getReferenceAt(42, 11).item;
487
+ ok(undescribedVar);
488
+ expect(undescribedVar.type.kind).to.equal('String');
489
+ const describedVar = jsdocsFile.getReferenceAt(44, 11).item;
490
+ ok(describedVar);
491
+ expect(describedVar.type.kind).to.equal('String');
492
+ }
493
+ function validateWithContexts(project) {
494
+ const complicatedScriptFile = project.getAssetByName('Complicated').gmlFile;
495
+ const withingScript = project.getAssetByName('Withing');
496
+ const withingScriptFile = withingScript.gmlFile;
497
+ ok(withingScript);
498
+ ok(withingScriptFile);
499
+ ok(complicatedScriptFile);
500
+ // WITHING INTO A GLOBAL CONSTRUCTED
501
+ const bschemaGlobalContext = complicatedScriptFile.getReferenceAt(1, 14).item
502
+ .type.type[0];
503
+ ok(bschemaGlobalContext &&
504
+ bschemaGlobalContext.kind === 'Struct' &&
505
+ bschemaGlobalContext.name === 'Bschema');
506
+ const withIntoBschemaGlobal = withingScriptFile.getScopeRangeAt(2, 15);
507
+ ok(withIntoBschemaGlobal &&
508
+ withIntoBschemaGlobal.self === bschemaGlobalContext);
509
+ // WITHING INTO AN OBJECT IDENTIFIER
510
+ const obj = project.getAssetByName('o_object');
511
+ ok(obj && obj.instanceType);
512
+ const withIntoObject = withingScriptFile.getScopeRangeAt(6, 16);
513
+ ok(withIntoObject && withIntoObject.self === obj.assetType);
514
+ // TYPE TEXT
515
+ const typeTextRef = withingScriptFile.getReferenceAt(10, 20);
516
+ ok(typeTextRef);
517
+ expect(typeTextRef.text).to.equal('Id.Instance.o_object');
518
+ expect(typeTextRef.isRenameable).to.equal(true);
519
+ expect(typeTextRef.toRenamed('new_name')).to.equal('Id.Instance.new_name');
520
+ // WITHING INTO AN OBJECT INSTANCE
521
+ const instanceVarRef = withingScriptFile.getReferenceAt(11, 11);
522
+ const instanceVar = instanceVarRef.item;
523
+ ok(instanceVar && instanceVar.type.type[0] === obj.instanceType);
524
+ expect(instanceVarRef.text).to.equal('o_instance');
525
+ expect(instanceVarRef.isRenameable).to.equal(true);
526
+ const withIntoInstance = withingScriptFile.getScopeRangeAt(13, 18);
527
+ ok(withIntoInstance && withIntoInstance.self === obj.instanceType);
528
+ // WITHING USING A JSDOC CONTEXT
529
+ const withIntoJsdoc = withingScriptFile.getScopeRangeAt(20, 25);
530
+ ok(withIntoJsdoc && withIntoJsdoc.self === obj.instanceType);
531
+ // WITHING INTO LOCAL STRUCT
532
+ const localStruct = withingScriptFile.getReferenceAt(24, 9).item;
533
+ ok(localStruct && localStruct.type.kind === 'Struct');
534
+ const withIntoLocalStruct = withingScriptFile.getScopeRangeAt(27, 16);
535
+ ok(withIntoLocalStruct &&
536
+ withIntoLocalStruct.self === localStruct.type.type[0]);
537
+ // The following JSDoc was misbehaving at one point...
538
+ const jsdoc = withingScriptFile.jsdocs[0];
539
+ ok(jsdoc);
540
+ expect(jsdoc.type?.content).to.equal('Id.Instance.o_object');
541
+ expect(jsdoc.type?.start.column).to.equal(12);
542
+ }
543
+ function validateBschemaConstructor(project) {
544
+ const complexScript = project.getAssetByName('Complicated');
545
+ const complexScriptFile = complexScript.gmlFile;
546
+ const bschemaGlobal = project.self.getMember('BSCHEMA');
547
+ const bschemaStructType = project.types.get('Struct.Bschema');
548
+ const bschemaGlobalDef = complexScriptFile.getReferenceAt(1, 15);
549
+ const bschemaConstructor = complexScriptFile.getReferenceAt(7, 13)
550
+ ?.item;
551
+ const bschemaRoleType = project.types.get('Struct.BschemaRole');
552
+ ok(bschemaGlobal);
553
+ ok(bschemaStructType);
554
+ ok(bschemaStructType.kind === 'Struct');
555
+ ok(bschemaGlobalDef);
556
+ ok(bschemaGlobalDef.item === bschemaGlobal);
557
+ ok(bschemaConstructor);
558
+ ok(bschemaConstructor.type.kind === 'Function');
559
+ expect(bschemaConstructor.type.type[0].name).to.equal('Bschema');
560
+ ok(bschemaConstructor.type.constructs[0] === bschemaStructType);
561
+ ok(bschemaRoleType);
562
+ // Check all of the members of Struct.Bschema.
563
+ // Make sure that the project_setup Bschema field gets typed based on its assignment
564
+ const projectSetupAssignedTo = bschemaConstructor.type.type[0].getParameter(0);
565
+ ok(projectSetupAssignedTo.name === 'project_setup_function');
566
+ // ok(projectSetupType === projectSetupAssignedTo.type);
567
+ // Check the types of all of the fields of the Bschema struct
568
+ const expectedKinds = {
569
+ base: { kind: 'Struct' },
570
+ changes: { kind: 'Any' },
571
+ clear_changes: { kind: 'Function' },
572
+ commit_id_prefix: { kind: 'String' },
573
+ commitId: { kind: 'String' },
574
+ create_commit_id: { kind: 'Function' },
575
+ create_role: { kind: 'Function' },
576
+ force_use_packed: { kind: 'Bool' },
577
+ init: { kind: 'Function' },
578
+ latest_commitId: {
579
+ kinds: ['String', 'Undefined'],
580
+ code: 'String|Undefined',
581
+ },
582
+ latest: { kind: 'String' },
583
+ next_commit_id: { kind: 'Function' },
584
+ packed_commitId: {
585
+ kinds: ['String', 'Undefined'],
586
+ code: 'String|Undefined',
587
+ },
588
+ project_setup: { kind: 'Function' },
589
+ roles: { kind: 'Struct', code: 'Struct<Struct.BschemaRole>' },
590
+ schema_mote_ids: { kind: 'Struct', code: 'Struct<Array<String>>' },
591
+ uid_pools: { kind: 'Struct' },
592
+ };
593
+ for (const [fieldName, info] of Object.entries(expectedKinds)) {
594
+ logger.log('Checking field', fieldName, 'of Bschema');
595
+ const member = bschemaStructType.getMember(fieldName);
596
+ const type = member?.type;
597
+ ok(member, `Expected to find member ${fieldName}`);
598
+ ok(type, `Expected to find type for member ${fieldName}`);
599
+ expect(member.name).to.equal(fieldName);
600
+ if ('kind' in info) {
601
+ expect(type.kind).to.equal(info.kind);
602
+ }
603
+ expect(member.def, 'All members should have a definition location').to
604
+ .exist;
605
+ if ('kinds' in info) {
606
+ expect(type?.type.length).to.equal(info.kinds.length);
607
+ for (const expectedKind of info.kinds) {
608
+ expect(type?.type.some((t) => t.kind === expectedKind), `Type ${expectedKind} not found in Union`).to.be.true;
609
+ }
610
+ }
611
+ if ('code' in info) {
612
+ expect(type.toFeatherString()).to.equal(info.code);
613
+ }
614
+ }
615
+ // Deeper checks on the types of some of the fields
616
+ //#region Bschema.schema_mote_ids
617
+ const schemaMoteIds = bschemaStructType.getMember('schema_mote_ids');
618
+ expect(schemaMoteIds.type.kind).to.equal('Struct');
619
+ expect(schemaMoteIds.type.items).to.exist;
620
+ expect(schemaMoteIds.type.items[0].kind).to.equal('Array');
621
+ expect(schemaMoteIds.type.items[0].type[0].items).to.exist;
622
+ expect(schemaMoteIds.type.items[0].type[0].items.kind).to.equal('String');
623
+ expect(schemaMoteIds.type.toFeatherString()).to.equal('Struct<Array<String>>');
624
+ //#endregion Bschema.schema_mote_ids
625
+ //#region Bschema.roles
626
+ const roles = bschemaStructType.getMember('roles');
627
+ expect(roles.type.kind).to.equal('Struct');
628
+ expect(roles.type.items[0]).to.exist;
629
+ expect(roles.type.items[0].kind).to.equal('Struct');
630
+ ok(roles.type.items[0].type[0] === bschemaRoleType);
631
+ //#endregion Bschema.roles
632
+ }
633
+ //# sourceMappingURL=project.test.js.map