gdharness 1.3.42 → 1.3.44

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 (106) hide show
  1. package/build/cli.js +383 -243
  2. package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
  3. package/build/godot/addons/gdharness_editor/animation_reads.gd +55 -33
  4. package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
  5. package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
  6. package/build/godot/addons/gdharness_editor/file_reads.gd +83 -42
  7. package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
  8. package/build/godot/addons/gdharness_editor/headless_check.gd +68 -44
  9. package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
  10. package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
  11. package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
  12. package/build/godot/addons/gdharness_editor/reading.gd +51 -17
  13. package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
  14. package/build/godot/addons/gdharness_editor/scene_file.gd +127 -80
  15. package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
  16. package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
  17. package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
  18. package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
  19. package/build/godot/addons/gdharness_editor/tools/animation_states.gd +37 -27
  20. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +79 -49
  21. package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
  22. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +65 -42
  23. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
  24. package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
  25. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
  26. package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
  27. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +166 -107
  28. package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
  29. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +68 -41
  30. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
  31. package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
  32. package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +12 -5
  33. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +113 -67
  34. package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
  35. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
  36. package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
  37. package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
  38. package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
  39. package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
  40. package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
  41. package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
  42. package/build/godot/addons/gdharness_runtime/reading.gd +51 -17
  43. package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
  44. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +149 -147
  45. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
  46. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
  47. package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
  48. package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
  49. package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
  50. package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
  51. package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
  52. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +34 -17
  53. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
  54. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
  55. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
  56. package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
  57. package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
  58. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
  59. package/build/godot/addons/gdharness_runtime/runtime_touch.gd +15 -15
  60. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
  61. package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
  62. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
  63. package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
  64. package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
  65. package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
  66. package/build/godot/addons/gdharness_tests/bench.gd +4 -5
  67. package/build/godot/addons/gdharness_tests/event.gd +10 -5
  68. package/build/godot/addons/gdharness_tests/share.gd +38 -18
  69. package/build/godot/addons/gdharness_tests/suites.gd +3 -3
  70. package/build/godot/addons/gdharness_tests/wait.gd +3 -2
  71. package/build/godot/loader/loader.gd +9 -9
  72. package/build/godot/operations/animation_reads.gd +55 -33
  73. package/build/godot/operations/audio_buses.gd +48 -19
  74. package/build/godot/operations/class_cache.gd +16 -11
  75. package/build/godot/operations/classdb_queries.gd +89 -44
  76. package/build/godot/operations/declared_values.gd +68 -32
  77. package/build/godot/operations/dependencies.gd +190 -109
  78. package/build/godot/operations/editor_tools.gd +10 -10
  79. package/build/godot/operations/export_presets.gd +100 -54
  80. package/build/godot/operations/file_reads.gd +83 -42
  81. package/build/godot/operations/file_walk.gd +12 -5
  82. package/build/godot/operations/gdscript_analysis.gd +86 -65
  83. package/build/godot/operations/gdscript_authoring.gd +83 -36
  84. package/build/godot/operations/godot_operations.gd +17 -11
  85. package/build/godot/operations/import_pipeline.gd +110 -59
  86. package/build/godot/operations/input_actions.gd +62 -39
  87. package/build/godot/operations/patterns.gd +3 -1
  88. package/build/godot/operations/plugins.gd +36 -16
  89. package/build/godot/operations/project_config.gd +61 -32
  90. package/build/godot/operations/project_diagnostics.gd +24 -11
  91. package/build/godot/operations/project_file.gd +16 -16
  92. package/build/godot/operations/project_validation.gd +71 -44
  93. package/build/godot/operations/property_paths.gd +22 -12
  94. package/build/godot/operations/property_values.gd +143 -78
  95. package/build/godot/operations/reading.gd +51 -17
  96. package/build/godot/operations/rename_checks.gd +2 -2
  97. package/build/godot/operations/resource_files.gd +11 -9
  98. package/build/godot/operations/same_values.gd +4 -4
  99. package/build/godot/operations/scene_connections.gd +27 -13
  100. package/build/godot/operations/script_check.gd +52 -49
  101. package/build/godot/operations/serialisation.gd +75 -51
  102. package/build/godot/operations/setting_reads.gd +7 -6
  103. package/build/godot/operations/value_shapes.gd +5 -6
  104. package/build/godot/tests/listed.gd +321 -268
  105. package/build/keeper.js +43 -43
  106. package/package.json +1 -1
@@ -1,6 +1,5 @@
1
1
  @tool
2
2
  extends Node
3
-
4
3
  ## Scenes and their nodes, edited in the open editor and read back from what it holds.
5
4
 
6
5
  const GdhDeclared = preload("../declared_values.gd")
@@ -46,8 +45,8 @@ func _kept(scene: GdhSceneFile, saved: Node, node: Node, names: Array) -> String
46
45
  var loaded: Variant = _properties.read_at(copy, property)
47
46
  var given: Variant = _properties.read_at(node, property)
48
47
  if not scene.kept(saved, loaded, given):
49
- # The file holds it, and the copy's own script set it again as the copy loaded, as it will
50
- # wherever the scene loads; an editor saves the same.
48
+ # The file holds it, and the copy's own script set it again as the copy loaded, as it
49
+ # will wherever the scene loads; an editor saves the same.
51
50
  if scene.stores(at, property, given):
52
51
  var on: Dictionary = scene.loads_as.get_or_add(at, {})
53
52
  if GdhHeadlessCheck.untooled(copy.get_script()):
@@ -56,8 +55,8 @@ func _kept(scene: GdhSceneFile, saved: Node, node: Node, names: Array) -> String
56
55
  continue
57
56
  return (
58
57
  (
59
- "%s.%s would load from the file as %s: the scene does not keep it, because the property "
60
- + "is not one a scene stores or the node belongs to another scene."
58
+ "%s.%s would load from the file as %s: the scene does not keep it, because the "
59
+ + "property is not one a scene stores or the node belongs to another scene."
61
60
  )
62
61
  % [at, property, JSON.stringify(_values.serialize_value(loaded), "", false)]
63
62
  )
@@ -151,7 +150,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
151
150
  var script_path: String = str(args.get("script", ""))
152
151
 
153
152
  if scene_path.is_empty():
154
- return {"ok": false, "error": "Missing scenePath"}
153
+ return { "ok": false, "error": "Missing scenePath" }
155
154
  var made: Dictionary = GdhSceneFile.made_as(
156
155
  root_node_type,
157
156
  "" if script_path.is_empty() else GdhSceneFile.res_path(project_path, script_path),
@@ -159,18 +158,21 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
159
158
  "rootNodeType"
160
159
  )
161
160
  if made.has("error"):
162
- return {"ok": false, "error": made["error"]}
161
+ return { "ok": false, "error": made["error"] }
163
162
  root_node_type = str(made["type"])
164
163
  script_path = str(made["script"])
165
164
  # A class that is not a node went into a Node variable, which is a runtime error the caller
166
165
  # heard about only as "Invalid tool result".
167
- if not ClassDB.class_exists(root_node_type) or not ClassDB.is_parent_class(root_node_type, "Node"):
168
- return {"ok": false, "error": GdhPlacing.not_a_node_class("rootNodeType", root_node_type)}
166
+ if (
167
+ not ClassDB.class_exists(root_node_type)
168
+ or not ClassDB.is_parent_class(root_node_type, "Node")
169
+ ):
170
+ return { "ok": false, "error": GdhPlacing.not_a_node_class("rootNodeType", root_node_type) }
169
171
  if not ClassDB.can_instantiate(root_node_type):
170
- return {"ok": false, "error": "%s cannot be instantiated." % root_node_type}
172
+ return { "ok": false, "error": "%s cannot be instantiated." % root_node_type }
171
173
  var unfit: String = GdhSceneFile.unfit_name(root_node_name)
172
174
  if not unfit.is_empty():
173
- return {"ok": false, "error": unfit}
175
+ return { "ok": false, "error": unfit }
174
176
  if FileAccess.file_exists(scene_path):
175
177
  return GdhKin.taken(scene_path)
176
178
 
@@ -186,29 +188,32 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
186
188
  var script: Script = load(full_script_path) as Script
187
189
  if script == null:
188
190
  root.free()
189
- return {"ok": false, "error": "No script loads from " + full_script_path}
191
+ return { "ok": false, "error": "No script loads from " + full_script_path }
190
192
  var uncompiled: String = GdhSceneFile.uncompiled(script, full_script_path)
191
193
  if not uncompiled.is_empty():
192
194
  root.free()
193
- return {"ok": false, "error": uncompiled}
195
+ return { "ok": false, "error": uncompiled }
194
196
  var base: String = str(script.get_instance_base_type())
195
197
  if not ClassDB.is_parent_class(root_node_type, base):
196
198
  root.free()
197
199
  return {
198
200
  "ok": false,
199
201
  "error":
200
- "%s extends %s, so it cannot be on a %s root." % [full_script_path, base, root_node_type]
202
+ (
203
+ "%s extends %s, so it cannot be on a %s root."
204
+ % [full_script_path, base, root_node_type]
205
+ ),
201
206
  }
202
207
  root.set_script(script)
203
- # Named again, since a headless engine runs the script's _init as it is attached, and a node's
204
- # name is no property the write compares.
208
+ # Named again, since a headless engine runs the script's _init as it is attached, and a
209
+ # node's name is no property the write compares.
205
210
  root.name = root_node_name
206
211
 
207
212
  var had_folder: bool = DirAccess.dir_exists_absolute(scene_path.get_base_dir())
208
213
  var folder: String = GdhSceneFile.make_folder_for(scene_path)
209
214
  if not folder.is_empty():
210
215
  root.free()
211
- return {"ok": false, "error": folder}
216
+ return { "ok": false, "error": folder }
212
217
  # Read before the write, which frees the root.
213
218
  var root_class: Dictionary = GdhSceneFile.class_of(root)
214
219
  var written: Dictionary = scene.write()
@@ -219,7 +224,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
219
224
  "ok": true,
220
225
  "scenePath": scene_path,
221
226
  "rootNodeType": root_class["nodeType"],
222
- "rootNodeName": root_node_name
227
+ "rootNodeName": root_node_name,
223
228
  }
224
229
  if root_class.has("scriptClass"):
225
230
  answer["scriptClass"] = root_class["scriptClass"]
@@ -239,7 +244,9 @@ func list_scene_nodes(args: Dictionary) -> Dictionary:
239
244
 
240
245
  # Read as a headless engine reads it with no editor connected, so the answer does not depend on
241
246
  # whether one is.
242
- var read: Dictionary = GdhFileReads.scene_tree(scene_path, depth, include_properties, include_defaults)
247
+ var read: Dictionary = GdhFileReads.scene_tree(
248
+ scene_path, depth, include_properties, include_defaults
249
+ )
243
250
  if read.get("ok", false):
244
251
  read["unsavedInEditor"] = GdhSceneFile.unsaved(scene_path)
245
252
  return read
@@ -256,12 +263,12 @@ func add_node(args: Dictionary) -> Dictionary:
256
263
  var adding: Array = GdhGroups.asked(args, "addGroups")
257
264
 
258
265
  if node_name.is_empty():
259
- return {"ok": false, "error": "Missing nodeName"}
266
+ return { "ok": false, "error": "Missing nodeName" }
260
267
  var class_script: Script = null
261
268
  if not node_type.is_empty() and not ClassDB.class_exists(node_type):
262
269
  var declared: Dictionary = GdhSceneFile.script_class(node_type, "Node")
263
270
  if declared.has("error"):
264
- return {"ok": false, "error": declared["error"]}
271
+ return { "ok": false, "error": declared["error"] }
265
272
  var held: Variant = declared.get("script")
266
273
  if held is Script:
267
274
  class_script = held
@@ -269,7 +276,7 @@ func add_node(args: Dictionary) -> Dictionary:
269
276
  var making: Array = GdhPlacing.what_to_add(node_type, instance_path, scene_path)
270
277
  var packed: PackedScene = making[0]
271
278
  if not str(making[1]).is_empty():
272
- return {"ok": false, "error": making[1]}
279
+ return { "ok": false, "error": making[1] }
273
280
 
274
281
  var opened: Array = _open(scene_path, true)
275
282
  var scene: GdhSceneFile = opened[0]
@@ -280,7 +287,9 @@ func add_node(args: Dictionary) -> Dictionary:
280
287
  var root: Node = scene.root
281
288
  var parent: Node = GdhSceneFile.node_at(root, parent_node_path)
282
289
  if not parent:
283
- return scene.refuse(scene.not_found("Parent node not found: " + parent_node_path, parent_node_path))
290
+ return scene.refuse(
291
+ scene.not_found("Parent node not found: " + parent_node_path, parent_node_path)
292
+ )
284
293
  var foreign: String = scene.foreign(parent)
285
294
  if not foreign.is_empty():
286
295
  return scene.refuse(foreign)
@@ -316,9 +325,9 @@ func add_node(args: Dictionary) -> Dictionary:
316
325
 
317
326
  var new_path: String = scene.path_of(new_node)
318
327
  # Read before the write, which frees the scene it was added to.
319
- var answer: Dictionary = {"ok": true, "nodeName": node_name}
328
+ var answer: Dictionary = { "ok": true, "nodeName": node_name }
320
329
  answer.merge(GdhSceneFile.class_of(new_node))
321
- answer.merge({"nodePath": new_path, "index": new_node.get_index()})
330
+ answer.merge({ "nodePath": new_path, "index": new_node.get_index() })
322
331
  if not properties.is_empty():
323
332
  var held: Dictionary = {}
324
333
  for key: Variant in properties:
@@ -331,7 +340,10 @@ func add_node(args: Dictionary) -> Dictionary:
331
340
  var written: Dictionary = scene.write(
332
341
  func(saved: Node) -> String:
333
342
  var unkept: String = _kept(
334
- scene, saved, new_node, properties.keys() + (["script"] if class_script != null else [])
343
+ scene,
344
+ saved,
345
+ new_node,
346
+ properties.keys() + (["script"] if class_script != null else [])
335
347
  )
336
348
  if unkept.is_empty() and saved.get_node_or_null(new_path) != null:
337
349
  unkept = GdhGroups.unkept(saved.get_node(new_path), adding, [])
@@ -357,9 +369,9 @@ func delete_node(args: Dictionary) -> Dictionary:
357
369
  "ok": false,
358
370
  "error":
359
371
  (
360
- "The root cannot be deleted, since a scene has exactly one. Delete the nodes under it, or "
361
- + "make a scene with another root with scene_create."
362
- )
372
+ "The root cannot be deleted, since a scene has exactly one. Delete the nodes under "
373
+ + "it, or make a scene with another root with scene_create."
374
+ ),
363
375
  }
364
376
 
365
377
  var opened: Array = _open(scene_path, true)
@@ -383,7 +395,7 @@ func delete_node(args: Dictionary) -> Dictionary:
383
395
  node.get_parent().remove_child(node)
384
396
  node.free()
385
397
  var carried: GdhPaths = GdhPaths.new()
386
- var _rewritten: int = carried.carry(scene, {at: ""})
398
+ var _rewritten: int = carried.carry(scene, { at: "" })
387
399
 
388
400
  var written: Dictionary = scene.write(
389
401
  func(saved: Node) -> String:
@@ -394,7 +406,7 @@ func delete_node(args: Dictionary) -> Dictionary:
394
406
  if not written.is_empty():
395
407
  return written
396
408
 
397
- var answer: Dictionary = {"ok": true, "deletedNodePath": at}
409
+ var answer: Dictionary = { "ok": true, "deletedNodePath": at }
398
410
  if not went.is_empty():
399
411
  answer["deletedWithIt"] = went
400
412
  answer.merge(GdhKin.follow(scene_path, at, "", carried))
@@ -409,7 +421,7 @@ func duplicate_node(args: Dictionary) -> Dictionary:
409
421
  var parent_path: String = str(args.get("parentNodePath", ""))
410
422
 
411
423
  if node_path.is_empty() or new_name.is_empty():
412
- return {"ok": false, "error": "Missing nodePath or newName"}
424
+ return { "ok": false, "error": "Missing nodePath or newName" }
413
425
 
414
426
  var opened: Array = _open(scene_path, true)
415
427
  var scene: GdhSceneFile = opened[0]
@@ -466,7 +478,9 @@ func duplicate_node(args: Dictionary) -> Dictionary:
466
478
  if copy == null:
467
479
  return "the copy would not be in the scene when it loads."
468
480
  if copy.get_index() != placed[1]:
469
- return "%s would load at index %d, not %d." % [new_path, copy.get_index(), placed[1]]
481
+ return (
482
+ "%s would load at index %d, not %d." % [new_path, copy.get_index(), placed[1]]
483
+ )
470
484
  return ""
471
485
  )
472
486
  if not written.is_empty():
@@ -489,9 +503,9 @@ func reparent_node(args: Dictionary) -> Dictionary:
489
503
  var new_parent_path: String = str(args.get("newParentPath", ""))
490
504
 
491
505
  if node_path.is_empty() or node_path == ".":
492
- return {"ok": false, "error": "Cannot reparent root node"}
506
+ return { "ok": false, "error": "Cannot reparent root node" }
493
507
  if new_parent_path.is_empty():
494
- return {"ok": false, "error": "Missing newParentPath"}
508
+ return { "ok": false, "error": "Missing newParentPath" }
495
509
 
496
510
  var opened: Array = _open(scene_path, true)
497
511
  var scene: GdhSceneFile = opened[0]
@@ -505,7 +519,9 @@ func reparent_node(args: Dictionary) -> Dictionary:
505
519
  if not node:
506
520
  return scene.refuse(scene.not_found("Node not found: " + node_path, node_path))
507
521
  if not new_parent:
508
- return scene.refuse(scene.not_found("New parent not found: " + new_parent_path, new_parent_path))
522
+ return scene.refuse(
523
+ scene.not_found("New parent not found: " + new_parent_path, new_parent_path)
524
+ )
509
525
  var fixed: String = scene.fixed(node)
510
526
  if not fixed.is_empty():
511
527
  return scene.refuse(fixed)
@@ -531,10 +547,12 @@ func reparent_node(args: Dictionary) -> Dictionary:
531
547
  var drawn_at: Dictionary = {}
532
548
  for property: String in GdhPlacing.placing(node):
533
549
  drawn_at[property] = _properties.read_at(node, property)
534
- var held_at: Transform3D = (node as Node3D).transform if node is Node3D else Transform3D.IDENTITY
550
+ var held_at: Transform3D = (
551
+ (node as Node3D).transform if node is Node3D else Transform3D.IDENTITY
552
+ )
535
553
  var stored_before: Dictionary = GdhHeadlessCheck.stored(node)
536
- # Unowned while it moves: a node taken out of the tree keeps its owner, and adding it under the new
537
- # parent warned that the owner was not above it. Node.reparent warns the same way.
554
+ # Unowned while it moves: a node taken out of the tree keeps its owner, and adding it under the
555
+ # new parent warned that the owner was not above it. Node.reparent warns the same way.
538
556
  node.owner = null
539
557
  old_parent.remove_child(node)
540
558
  new_parent.add_child(node)
@@ -551,18 +569,21 @@ func reparent_node(args: Dictionary) -> Dictionary:
551
569
  for property: String in drawn_at:
552
570
  var now: Variant = _properties.read_at(node, property)
553
571
  if not GdhSame.same(now, drawn_at[property]):
554
- also_changed[property] = {"was": _readable(node, drawn_at[property]), "now": _readable(node, now)}
572
+ also_changed[property] = {
573
+ "was": _readable(node, drawn_at[property]), "now": _readable(node, now),
574
+ }
555
575
  var sheared: String = ""
556
576
  if node is Node3D and GdhPlacing.sheared((node as Node3D).basis):
557
577
  var spatial: Node3D = node
558
578
  also_changed["transform"] = {
559
- "was": _readable(node, held_at), "now": _readable(node, spatial.transform)
579
+ "was": _readable(node, held_at), "now": _readable(node, spatial.transform),
560
580
  }
561
581
  sheared = (
562
582
  (
563
- "%s keeps where it is drawn under %s, which is scaled unevenly across the way it is turned,"
564
- + " so its basis is sheared: no position, rotation and scale describe it, and transform is"
565
- + " what it holds. keepGlobalTransform false keeps its own transform instead."
583
+ "%s keeps where it is drawn under %s, which is scaled unevenly across the way it "
584
+ + "is turned, so its basis is sheared: no position, rotation and scale describe "
585
+ + "it, and transform is what it holds. keepGlobalTransform false keeps its own "
586
+ + "transform instead."
566
587
  )
567
588
  % [str(node.name), new_parent_path]
568
589
  )
@@ -570,7 +591,10 @@ func reparent_node(args: Dictionary) -> Dictionary:
570
591
  var new_path: String = scene.path_of(node)
571
592
  var index: int = node.get_index()
572
593
  var carried: GdhPaths = GdhPaths.new()
573
- var _rewritten: int = carried.carry(scene, {old_path: new_path} if new_path != old_path else {})
594
+ var _rewritten: int = carried.carry(
595
+ scene,
596
+ { old_path: new_path } if new_path != old_path else {},
597
+ )
574
598
  var written: Dictionary = scene.write(
575
599
  func(saved: Node) -> String:
576
600
  var copy: Node = saved.get_node_or_null(new_path)
@@ -611,7 +635,10 @@ func set_node_properties(args: Dictionary) -> Dictionary:
611
635
  var adding: Array = GdhGroups.asked(args, "addGroups")
612
636
  var removing: Array = GdhGroups.asked(args, "removeGroups")
613
637
  if properties.is_empty() and editable == null and adding.is_empty() and removing.is_empty():
614
- return {"ok": false, "error": "set takes properties, editableChildren, addGroups or removeGroups."}
638
+ return {
639
+ "ok": false,
640
+ "error": "set takes properties, editableChildren, addGroups or removeGroups.",
641
+ }
615
642
 
616
643
  var opened: Array = _open(scene_path, true)
617
644
  var scene: GdhSceneFile = opened[0]
@@ -661,7 +688,7 @@ func set_node_properties(args: Dictionary) -> Dictionary:
661
688
  for key: Variant in properties:
662
689
  held[str(key)] = _readable(node, _properties.read_at(node, str(key)))
663
690
  var new_path: String = scene.path_of(node)
664
- var answer: Dictionary = {"ok": true, "nodePath": new_path, "properties": held}
691
+ var answer: Dictionary = { "ok": true, "nodePath": new_path, "properties": held }
665
692
  _properties.note_write(answer, node)
666
693
  var shared: Dictionary = {}
667
694
  for key: Variant in properties:
@@ -679,7 +706,10 @@ func set_node_properties(args: Dictionary) -> Dictionary:
679
706
  named.append(property)
680
707
  var source: String = scene.values_from(node, old_path)
681
708
  var carried: GdhPaths = GdhPaths.new()
682
- var _rewritten: int = carried.carry(scene, {old_path: new_path} if new_path != old_path else {})
709
+ var _rewritten: int = carried.carry(
710
+ scene,
711
+ { old_path: new_path } if new_path != old_path else {},
712
+ )
683
713
  var id: int = node.get_instance_id()
684
714
  var written: Dictionary = scene.write(
685
715
  func(saved: Node) -> String:
@@ -687,9 +717,13 @@ func set_node_properties(args: Dictionary) -> Dictionary:
687
717
  if unkept.is_empty():
688
718
  unkept = GdhGroups.unkept(saved.get_node(new_path), adding, removing)
689
719
  if unkept.is_empty() and editable != null:
690
- if saved.is_editable_instance(saved.get_node(new_path)) != GdhRead.as_bool(editable):
720
+ if (
721
+ saved.is_editable_instance(saved.get_node(new_path))
722
+ != GdhRead.as_bool(editable)
723
+ ):
691
724
  return (
692
- "%s would load with editableChildren %s." % [new_path, not GdhRead.as_bool(editable)]
725
+ "%s would load with editableChildren %s."
726
+ % [new_path, not GdhRead.as_bool(editable)]
693
727
  )
694
728
  return unkept
695
729
  )
@@ -722,16 +756,16 @@ static func _note_loads_as(answer: Dictionary, scene: GdhSceneFile, at: String)
722
756
  if scene.loads_untooled.has(at):
723
757
  answer["loadsAsNote"] = (
724
758
  (
725
- "The file holds %s as given, which the editor shows. %s's script is not a tool, so in the "
726
- + "game it runs as the scene loads and changes %s to what loadsAs gives."
759
+ "The file holds %s as given, which the editor shows. %s's script is not a tool, so "
760
+ + "in the game it runs as the scene loads and changes %s to what loadsAs gives."
727
761
  )
728
762
  % [", ".join(names), at, pronoun]
729
763
  )
730
764
  else:
731
765
  answer["loadsAsNote"] = (
732
766
  (
733
- "The file holds %s as given. %s's script runs as the scene loads, in the editor and in "
734
- + "the game, and changes %s to what loadsAs gives."
767
+ "The file holds %s as given. %s's script runs as the scene loads, in the editor "
768
+ + "and in the game, and changes %s to what loadsAs gives."
735
769
  )
736
770
  % [", ".join(names), at, pronoun]
737
771
  )
@@ -773,8 +807,8 @@ static func _note_left_out(
773
807
  else:
774
808
  answer["notStoredNote"] = (
775
809
  (
776
- "%s now %s what %s gives this node, so the file does not store %s, and a change there reaches "
777
- + "this node."
810
+ "%s now %s what %s gives this node, so the file does not store %s, and a change "
811
+ + "there reaches this node."
778
812
  )
779
813
  % [names, verb, source, pronoun]
780
814
  )
@@ -806,7 +840,7 @@ func get_node_properties(args: Dictionary) -> Dictionary:
806
840
  var at: String = scene.path_of(node)
807
841
  var here: Array = set_here.keys()
808
842
  here.sort()
809
- var answer: Dictionary = {"ok": true, "nodePath": at, "properties": props, "setHere": here}
843
+ var answer: Dictionary = { "ok": true, "nodePath": at, "properties": props, "setHere": here }
810
844
  # A Node3D stores only its transform, a matrix, while a set takes the position, rotation and
811
845
  # scale the inspector shows, so a node turned 45 degrees down read back as nine numbers. A
812
846
  # sheared one has no such three, and is left to its transform.
@@ -827,7 +861,9 @@ func get_node_properties(args: Dictionary) -> Dictionary:
827
861
  # A player's getter answers Master for a bus the layout does not have, while the file keeps the
828
862
  # name, so a player left on a renamed or removed bus read as one put on Master.
829
863
  var bus: String = str(set_here.get("bus", ""))
830
- var plays: bool = node is AudioStreamPlayer or node is AudioStreamPlayer2D or node is AudioStreamPlayer3D
864
+ var plays: bool = (
865
+ node is AudioStreamPlayer or node is AudioStreamPlayer2D or node is AudioStreamPlayer3D
866
+ )
831
867
  if plays and not bus.is_empty() and AudioServer.get_bus_index(bus) == -1:
832
868
  props["bus"] = bus
833
869
  var buses: PackedStringArray = []
@@ -835,10 +871,15 @@ func get_node_properties(args: Dictionary) -> Dictionary:
835
871
  var _added: bool = buses.append(AudioServer.get_bus_name(index))
836
872
  answer["busNote"] = (
837
873
  (
838
- "%s names the bus %s, which the project's bus layout does not have, so it plays on %s; "
839
- + "the buses are %s."
874
+ "%s names the bus %s, which the project's bus layout does not have, so it plays on "
875
+ + "%s; the buses are %s."
840
876
  )
841
- % [str(node.name) if at == "." else at, bus, AudioServer.get_bus_name(0), ", ".join(buses)]
877
+ % [
878
+ str(node.name) if at == "." else at,
879
+ bus,
880
+ AudioServer.get_bus_name(0),
881
+ ", ".join(buses),
882
+ ]
842
883
  )
843
884
  # A mask reads as a number, and which layers it holds, under the names the project gives them,
844
885
  # is what a caller reads it for.
@@ -902,7 +943,7 @@ func save_scene(args: Dictionary) -> Dictionary:
902
943
  if target_path != scene_path:
903
944
  var unsaved: String = GdhSceneFile.unsaved_in_editor(target_path)
904
945
  if not unsaved.is_empty():
905
- return {"ok": false, "error": unsaved}
946
+ return { "ok": false, "error": unsaved }
906
947
 
907
948
  var open_root: Node = null
908
949
  var open_roots: Array[Node] = []
@@ -922,7 +963,9 @@ func save_scene(args: Dictionary) -> Dictionary:
922
963
  var packed: PackedScene = PackedScene.new()
923
964
  var packing: Error = packed.pack(open_root)
924
965
  if packing != OK:
925
- return {"ok": false, "error": "Failed to pack %s: %s" % [scene_path, error_string(packing)]}
966
+ return {
967
+ "ok": false, "error": "Failed to pack %s: %s" % [scene_path, error_string(packing)],
968
+ }
926
969
  written = GdhSceneFile.save_packed(packed, target_path)
927
970
  else:
928
971
  var scene: GdhSceneFile = GdhSceneFile.new()
@@ -938,7 +981,7 @@ func save_scene(args: Dictionary) -> Dictionary:
938
981
  "ok": true,
939
982
  "scenePath": scene_path,
940
983
  "savedPath": target_path,
941
- "from": "editor" if open_root != null else "file"
984
+ "from": "editor" if open_root != null else "file",
942
985
  }
943
986
 
944
987
 
@@ -952,16 +995,19 @@ func _save_open_scene(scene_path: String) -> Dictionary:
952
995
  EditorInterface.open_scene_from_path(scene_path)
953
996
  var now: Node = EditorInterface.get_edited_scene_root()
954
997
  if now == null or now.scene_file_path != scene_path:
955
- return {"ok": false, "error": "The editor did not switch to %s to save it." % scene_path}
998
+ return { "ok": false, "error": "The editor did not switch to %s to save it." % scene_path }
956
999
  var saving: Error = EditorInterface.save_scene()
957
1000
  if not previous.is_empty() and previous != scene_path:
958
1001
  EditorInterface.open_scene_from_path(previous)
959
1002
  if saving != OK:
960
- return {"ok": false, "error": "The editor failed to save %s: %s" % [scene_path, error_string(saving)]}
1003
+ return {
1004
+ "ok": false,
1005
+ "error": "The editor failed to save %s: %s" % [scene_path, error_string(saving)],
1006
+ }
961
1007
  return {
962
1008
  "ok": true,
963
1009
  "scenePath": scene_path,
964
1010
  "savedPath": scene_path,
965
1011
  "from": "editor",
966
- "unsavedInEditor": GdhSceneFile.unsaved(scene_path)
1012
+ "unsavedInEditor": GdhSceneFile.unsaved(scene_path),
967
1013
  }
@@ -1,18 +1,21 @@
1
1
  @tool
2
2
  extends RefCounted
3
-
4
3
  ## The node paths a GDScript names in its own source, from the node it is on: `$Path`, `%Unique`,
5
4
  ## and a string handed to get_node, get_node_or_null or has_node called on itself. Read from the
6
5
  ## tokens rather than the lines, so a `$` or a `%` inside a string or a comment names nothing, and
7
6
  ## a `%` after an operand is the remainder of a division rather than a unique name.
8
7
 
9
- enum { NAME, STRING, MARK }
8
+ enum {
9
+ NAME,
10
+ STRING,
11
+ MARK,
12
+ }
10
13
 
11
14
  ## The calls that take a path from the node they are called on.
12
15
  const CALLS: Array[String] = ["get_node", "get_node_or_null", "has_node"]
13
16
  ## The words after which a `%` starts an operand.
14
17
  const OPERAND_AFTER: Array[String] = [
15
- "return", "and", "or", "not", "in", "if", "elif", "while", "await", "match", "when", "else"
18
+ "return", "and", "or", "not", "in", "if", "elif", "while", "await", "match", "when", "else",
16
19
  ]
17
20
  ## The marks after which a `%` starts an operand: an opening bracket, a separator or an operator.
18
21
  const OPERAND_AFTER_MARKS: String = "([{,=:;+-*/<>!&|^~%"
@@ -33,10 +36,14 @@ static func named(source: String) -> Array[Dictionary]:
33
36
  elif _is_mark(token, "%") and _starts_operand(tokens, at):
34
37
  path = _path_from(tokens, at)
35
38
  elif token[0] == NAME and CALLS.has(str(token[1])) and _on_itself(tokens, at):
36
- if at + 2 < tokens.size() and _is_mark(tokens[at + 1], "(") and tokens[at + 2][0] == STRING:
39
+ if (
40
+ at + 2 < tokens.size()
41
+ and _is_mark(tokens[at + 1], "(")
42
+ and tokens[at + 2][0] == STRING
43
+ ):
37
44
  path = str(tokens[at + 2][1])
38
45
  if not path.is_empty():
39
- found.append({"line": token[2], "path": path})
46
+ found.append({ "line": token[2], "path": path })
40
47
  return found
41
48
 
42
49
 
@@ -58,7 +65,8 @@ static func _path_from(tokens: Array[Array], at: int) -> String:
58
65
  return "/".join(parts)
59
66
 
60
67
 
61
- ## The name of a path at [param at], as [its text, where the next token is], or [] when there is none.
68
+ ## The name of a path at [param at], as [its text, where the next token is], or [] when there is
69
+ ## none.
62
70
  static func _part_at(tokens: Array[Array], at: int) -> Array:
63
71
  var unique: String = ""
64
72
  if at < tokens.size() and _is_mark(tokens[at], "%"):
@@ -112,7 +120,10 @@ static func _tokens(source: String) -> Array[Array]:
112
120
  at += 1
113
121
  elif c in " \t\r":
114
122
  at += 1
115
- elif QUOTES.contains(c) or ("r^&".contains(c) and QUOTES.contains(next) and not next.is_empty()):
123
+ elif (
124
+ QUOTES.contains(c)
125
+ or ("r^&".contains(c) and QUOTES.contains(next) and not next.is_empty())
126
+ ):
116
127
  var quoted: Array = _string(source, at if QUOTES.contains(c) else at + 1)
117
128
  tokens.append([STRING, quoted[0], line])
118
129
  line += str(quoted[0]).count("\n")
@@ -133,7 +144,8 @@ static func _tokens(source: String) -> Array[Array]:
133
144
 
134
145
 
135
146
  ## The string whose opening quote is at [param at], as [what is between its quotes, where it ends].
136
- ## A backslash keeps the character after it inside the string, raw or not, as the tokenizer reads it.
147
+ ## A backslash keeps the character after it inside the string, raw or not, as the tokenizer reads
148
+ ## it.
137
149
  static func _string(source: String, at: int) -> Array:
138
150
  var quote: String = source[at]
139
151
  var closing: String = quote.repeat(3) if source.substr(at, 3) == quote.repeat(3) else quote
@@ -1,6 +1,5 @@
1
1
  @tool
2
2
  extends Node
3
-
4
3
  ## Project settings read from the open editor, for a caller that asked for the editor's answer, and
5
4
  ## taken up by it after a write to the file.
6
5
  ##
@@ -29,7 +28,9 @@ func get_project_setting(args: Dictionary) -> Dictionary:
29
28
 
30
29
  var setting: String = str(args.get("setting", ""))
31
30
  if setting.is_empty():
32
- return {"ok": false, "error": "setting is required, or prefix to read every setting under one"}
31
+ return {
32
+ "ok": false, "error": "setting is required, or prefix to read every setting under one",
33
+ }
33
34
 
34
35
  var result: Dictionary = GdhSettingReads.one(setting, _values)
35
36
  result["ok"] = true
@@ -46,11 +47,11 @@ func adopt_project_settings(args: Dictionary) -> Dictionary:
46
47
  var file: ConfigFile = ConfigFile.new()
47
48
  var loaded: Error = file.load("res://project.godot")
48
49
  if loaded != OK:
49
- return {"ok": false, "error": "Could not read project.godot: " + error_string(loaded)}
50
+ return { "ok": false, "error": "Could not read project.godot: " + error_string(loaded) }
50
51
  var adopted: Array[String] = []
51
52
  var named: Variant = args.get("settings", [])
52
53
  if not named is Array:
53
- return {"ok": false, "error": "settings must be a list of setting names"}
54
+ return { "ok": false, "error": "settings must be a list of setting names" }
54
55
  var settings: Array = named
55
56
  for entry: Variant in settings:
56
57
  var setting: String = str(entry)
@@ -66,19 +67,21 @@ func adopt_project_settings(args: Dictionary) -> Dictionary:
66
67
  else:
67
68
  ProjectSettings.set_setting(setting, null)
68
69
  adopted.append(setting)
69
- return {"ok": true, "adopted": adopted}
70
+ return { "ok": true, "adopted": adopted }
70
71
 
71
72
 
72
73
  ## Loads the bus layout the project names into the editor's audio server, after something else
73
74
  ## wrote it: the editor holds the layout it loaded and saves that one when its audio panel changes.
74
75
  func adopt_audio_bus_layout(_args: Dictionary) -> Dictionary:
75
76
  var path: String = str(
76
- ProjectSettings.get_setting("audio/buses/default_bus_layout", "res://default_bus_layout.tres")
77
+ ProjectSettings.get_setting(
78
+ "audio/buses/default_bus_layout", "res://default_bus_layout.tres"
79
+ )
77
80
  )
78
81
  var layout: AudioBusLayout = ResourceLoader.load(
79
82
  path, "AudioBusLayout", ResourceLoader.CACHE_MODE_REPLACE
80
83
  )
81
84
  if layout == null:
82
- return {"ok": false, "error": "Could not load the bus layout at " + path}
85
+ return { "ok": false, "error": "Could not load the bus layout at " + path }
83
86
  AudioServer.set_bus_layout(layout)
84
- return {"ok": true, "layout": path}
87
+ return { "ok": true, "layout": path }