gdharness 1.3.41 → 1.3.43

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 (107) hide show
  1. package/build/cli.js +131 -47
  2. package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
  3. package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
  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 +114 -39
  7. package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
  8. package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
  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 +37 -12
  13. package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
  14. package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
  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 +41 -28
  20. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
  21. package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
  22. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
  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 +181 -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 +75 -43
  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 +66 -0
  33. package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
  34. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
  35. package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
  36. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
  37. package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
  38. package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
  39. package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
  40. package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
  41. package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
  42. package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
  43. package/build/godot/addons/gdharness_runtime/reading.gd +37 -12
  44. package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
  45. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
  46. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
  47. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
  48. package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
  49. package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
  50. package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
  51. package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
  52. package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
  53. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +38 -20
  54. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
  55. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
  56. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
  57. package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
  58. package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
  59. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
  60. package/build/godot/addons/gdharness_runtime/runtime_touch.gd +211 -0
  61. package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
  62. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
  63. package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
  64. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
  65. package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
  66. package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
  67. package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
  68. package/build/godot/addons/gdharness_tests/bench.gd +4 -5
  69. package/build/godot/addons/gdharness_tests/event.gd +10 -5
  70. package/build/godot/addons/gdharness_tests/share.gd +38 -18
  71. package/build/godot/addons/gdharness_tests/suites.gd +3 -3
  72. package/build/godot/addons/gdharness_tests/wait.gd +3 -2
  73. package/build/godot/loader/loader.gd +9 -9
  74. package/build/godot/operations/animation_reads.gd +84 -35
  75. package/build/godot/operations/audio_buses.gd +48 -19
  76. package/build/godot/operations/class_cache.gd +16 -11
  77. package/build/godot/operations/classdb_queries.gd +89 -44
  78. package/build/godot/operations/declared_values.gd +68 -32
  79. package/build/godot/operations/dependencies.gd +139 -93
  80. package/build/godot/operations/editor_tools.gd +9 -10
  81. package/build/godot/operations/export_presets.gd +100 -54
  82. package/build/godot/operations/file_reads.gd +114 -39
  83. package/build/godot/operations/file_walk.gd +12 -5
  84. package/build/godot/operations/gdscript_analysis.gd +105 -66
  85. package/build/godot/operations/gdscript_authoring.gd +138 -39
  86. package/build/godot/operations/godot_operations.gd +17 -11
  87. package/build/godot/operations/import_pipeline.gd +110 -59
  88. package/build/godot/operations/input_actions.gd +63 -39
  89. package/build/godot/operations/patterns.gd +3 -1
  90. package/build/godot/operations/plugins.gd +36 -16
  91. package/build/godot/operations/project_config.gd +144 -27
  92. package/build/godot/operations/project_diagnostics.gd +24 -11
  93. package/build/godot/operations/project_file.gd +16 -16
  94. package/build/godot/operations/project_validation.gd +71 -44
  95. package/build/godot/operations/property_paths.gd +22 -12
  96. package/build/godot/operations/property_values.gd +143 -78
  97. package/build/godot/operations/reading.gd +37 -12
  98. package/build/godot/operations/rename_checks.gd +2 -2
  99. package/build/godot/operations/resource_files.gd +11 -9
  100. package/build/godot/operations/same_values.gd +4 -4
  101. package/build/godot/operations/scene_connections.gd +27 -13
  102. package/build/godot/operations/script_check.gd +52 -49
  103. package/build/godot/operations/serialisation.gd +75 -51
  104. package/build/godot/operations/setting_reads.gd +7 -6
  105. package/build/godot/operations/value_shapes.gd +5 -6
  106. package/build/godot/tests/listed.gd +321 -268
  107. package/package.json +1 -1
@@ -1,11 +1,10 @@
1
1
  @tool
2
2
  extends RefCounted
3
-
4
- ## What a write made by a headless engine has to put back so the file holds what an editor would have
5
- ## written.
3
+ ## What a write made by a headless engine has to put back so the file holds what an editor would
4
+ ## have written.
6
5
  ##
7
- ## The editor runs only tool scripts and gives every other script a placeholder that holds values and
8
- ## runs nothing. A headless engine runs them all, so loading a file runs each script's
6
+ ## The editor runs only tool scripts and gives every other script a placeholder that holds values
7
+ ## and runs nothing. A headless engine runs them all, so loading a file runs each script's
9
8
  ## [code]_init[/code] and every setter of a stored property, and a tool setting one runs its setter.
10
9
  ## Whatever those change is saved with the write unless it is put back. Godot's own setters run in
11
10
  ## both, so a property a built-in setter derives (a Control's offsets from its size) is left alone
@@ -14,10 +13,9 @@ extends RefCounted
14
13
  const GdhPropertyPaths = preload("property_paths.gd")
15
14
  const GdhRead = preload("reading.gd")
16
15
  const GdhSame = preload("same_values.gd")
17
-
18
16
  const REMEDY: String = (
19
- "Open the project's editor (editor_launch with hidden: true) and ask again: the write then goes "
20
- + "through it, and the editor runs no script that is not a tool."
17
+ "Open the project's editor (editor_launch with hidden: true) and ask again: the write then "
18
+ + "goes through it, and the editor runs no script that is not a tool."
21
19
  )
22
20
 
23
21
 
@@ -76,7 +74,7 @@ static func kept_by_engine(object: Object, name: String, wanted: Variant, held:
76
74
  not GdhSame.same(object.get(name), wanted)
77
75
  and GdhSame.same(object.get(name), held)
78
76
  and name != "script"
79
- and not through_script(object, {name: true})
77
+ and not through_script(object, { name: true })
80
78
  )
81
79
 
82
80
 
@@ -155,7 +153,7 @@ static func reached(object: Object, property: String) -> Dictionary:
155
153
  name = name.get_slice(":", 0)
156
154
  var id: String = holder.get_scene_unique_id()
157
155
  if not id.is_empty():
158
- ids[id] = {name: true}
156
+ ids[id] = { name: true }
159
157
  return ids
160
158
 
161
159
 
@@ -175,7 +173,8 @@ static func spare(into: Dictionary, more: Dictionary) -> void:
175
173
  into[id] = given
176
174
 
177
175
 
178
- ## Whether [param object] holds, at any depth, an embedded resource with a script that is not a tool.
176
+ ## Whether [param object] holds, at any depth, an embedded resource with a script that is not a
177
+ ## tool.
179
178
  static func holds_scripted_embedded(object: Object, seen: Dictionary) -> bool:
180
179
  for value: Variant in stored(object).values():
181
180
  for resource: Resource in resources_in(value):
@@ -219,7 +218,9 @@ static func put_back(file_path: String, previous: Variant) -> String:
219
218
  return " Putting the file back as it was failed too."
220
219
  file.close()
221
220
  if ResourceLoader.has_cached(file_path):
222
- var _replaced: Resource = ResourceLoader.load(file_path, "", ResourceLoader.CACHE_MODE_REPLACE)
221
+ var _replaced: Resource = ResourceLoader.load(
222
+ file_path, "", ResourceLoader.CACHE_MODE_REPLACE
223
+ )
223
224
  elif DirAccess.remove_absolute(ProjectSettings.globalize_path(file_path)) != OK:
224
225
  return " Removing the file it wrote failed too."
225
226
  return ""
@@ -235,14 +236,14 @@ static func sections(text: String) -> Dictionary:
235
236
  for line: String in text.split("\n"):
236
237
  if line.begins_with("["):
237
238
  if not key.is_empty():
238
- found[key] = {"head": head, "lines": lines}
239
+ found[key] = { "head": head, "lines": lines }
239
240
  lines = []
240
241
  head = line
241
242
  key = _section_key(line)
242
243
  continue
243
244
  var _kept: bool = lines.append(line)
244
245
  if not key.is_empty():
245
- found[key] = {"head": head, "lines": lines}
246
+ found[key] = { "head": head, "lines": lines }
246
247
  return found
247
248
 
248
249
 
@@ -257,8 +258,8 @@ static func _section_key(head: String) -> String:
257
258
  return ""
258
259
 
259
260
 
260
- ## Each property a section sets, by name, to the text of its value. A value running over several lines
261
- ## is kept whole.
261
+ ## Each property a section sets, by name, to the text of its value. A value running over several
262
+ ## lines is kept whole.
262
263
  static func entries_of(section: Dictionary) -> Dictionary:
263
264
  var lines: PackedStringArray = section.get("lines", PackedStringArray())
264
265
  return entries(lines)
@@ -315,7 +316,8 @@ static func _main_entries(text: String) -> Dictionary:
315
316
  return entries_of(main)
316
317
 
317
318
 
318
- ## The ids of the embedded resources in [param text] that carry a script that is not a tool, as a set.
319
+ ## The ids of the embedded resources in [param text] that carry a script that is not a tool, as a
320
+ ## set.
319
321
  static func scripted_sections(text: String) -> Dictionary:
320
322
  var all: Dictionary = sections(text)
321
323
  var scripts: Dictionary = {}
@@ -349,7 +351,9 @@ static func scripted_sections(text: String) -> Dictionary:
349
351
  ## null]. Only those with a script that is not a tool are compared, since only a script runs
350
352
  ## differently here, and what [param reached_ids] says the tool wrote is left out, as
351
353
  ## [method reached] answers it.
352
- static func embedded_changes(before: String, after: String, reached_ids: Dictionary) -> Array[Array]:
354
+ static func embedded_changes(
355
+ before: String, after: String, reached_ids: Dictionary
356
+ ) -> Array[Array]:
353
357
  var changed: Array[Array] = []
354
358
  if before.is_empty():
355
359
  return changed
@@ -375,34 +379,37 @@ static func embedded_changes(before: String, after: String, reached_ids: Diction
375
379
  return changed
376
380
 
377
381
 
378
- ## Compares the file just saved at [param path] with [param before], what it held, and puts back
379
- ## what a script that is not a tool changed in it: in its embedded resources, which [param values]
380
- ## reach, and in [param main], its main resource, unless that is null. [param asked] and
381
- ## [param reached_ids] are what the call changed, and [param carried] is [method embedded_in] of
382
- ## [param values] taken before the save. Saved again through [param resave], which answers an Error,
383
- ## and answers "" or why the file still does not hold what it should.
382
+ ## Compares the file [param saved] names with what it held before, and puts back what a script that
383
+ ## is not a tool changed in it: in its embedded resources, which [param values] reach, and in
384
+ ## [param main], its main resource, unless that is null. [param asked] and [param reached_ids] are
385
+ ## what the call changed, and [param carried] is [method embedded_in] of [param values] taken before
386
+ ## the save. Saved again through [param saved], and answers "" or why the file still does not hold
387
+ ## what it should.
384
388
  ##
385
389
  ## An id is compared only where the resource holding it after the save held it before. Each headless
386
390
  ## engine hands out the ids of one sequence, so a resource the call added can be saved under the id
387
391
  ## the file had kept for one it dropped. Read as one resource, the new third line of a dialogue was
388
392
  ## taken for the old first one changed by its script, and given the old one's text back.
389
393
  static func settle_saved(
390
- path: String,
391
- before: String,
394
+ saved: GdhSaved,
392
395
  values: Array,
393
396
  main: Object,
394
397
  asked: Dictionary,
395
398
  reached_ids: Dictionary,
396
- carried: Dictionary,
397
- resave: Callable
399
+ carried: Dictionary
398
400
  ) -> String:
401
+ var path: String = saved.path
402
+ var before: String = saved.before
399
403
  for attempt: int in 2:
400
404
  var after: String = FileAccess.get_file_as_string(path)
401
405
  var by_id: Dictionary = embedded_in(values, {})
402
406
  var embedded: Array[Array] = []
403
407
  for found: Array in embedded_changes(before, after, reached_ids):
404
408
  var holder: Resource = by_id.get(found[0])
405
- if not carried.has(found[0]) or (holder != null and not is_same(holder, carried[found[0]])):
409
+ if (
410
+ not carried.has(found[0])
411
+ or (holder != null and not is_same(holder, carried[found[0]]))
412
+ ):
406
413
  continue
407
414
  if holder != null and _unchanged(holder, found.slice(1)):
408
415
  continue
@@ -439,7 +446,7 @@ static func settle_saved(
439
446
  var refused: String = put_back_value(main, name, was_set, held)
440
447
  if not refused.is_empty():
441
448
  return unasked(path, name, refused)
442
- var saving: Error = resave.call()
449
+ var saving: Error = saved.resave.call()
443
450
  if saving != OK:
444
451
  return "Failed to save %s again: %s." % [path, error_string(saving)]
445
452
  return ""
@@ -474,19 +481,22 @@ static func put_back_value(object: Object, name: String, was_set: bool, held: St
474
481
  object.set(name, default_of(object, name))
475
482
  return ""
476
483
  if "Resource(" in held:
477
- return "its value there names another resource, which cannot be restored from the text here."
484
+ return (
485
+ "its value there names another resource, which cannot be restored from the text "
486
+ + "here."
487
+ )
478
488
  object.set(name, str_to_var(held))
479
489
  return ""
480
490
 
481
491
 
482
- ## [param refused], a refusal of [param property] on [param object] for not holding what it was given,
483
- ## with why when a headless engine set it through a script that is not a tool. That setter runs here
484
- ## and in the game, and an editor runs none and saves the value given, so a setter clamping stock to
485
- ## 0 refused -5 here and the same call through an editor wrote it.
492
+ ## [param refused], a refusal of [param property] on [param object] for not holding what it was
493
+ ## given, with why when a headless engine set it through a script that is not a tool. That setter
494
+ ## runs here and in the game, and an editor runs none and saves the value given, so a setter
495
+ ## clamping stock to 0 refused -5 here and the same call through an editor wrote it.
486
496
  static func setter_ran(object: Object, property: String, refused: String) -> String:
487
497
  if Engine.is_editor_hint() or property.is_empty():
488
498
  return refused
489
- if not through_script(object, {property.get_slice(":", 0): true}):
499
+ if not through_script(object, { property.get_slice(":", 0): true }):
490
500
  return refused
491
501
  var attached: Variant = object.get_script()
492
502
  var named: String = "Its script"
@@ -496,8 +506,9 @@ static func setter_ran(object: Object, property: String, refused: String) -> Str
496
506
  return (
497
507
  (
498
508
  (
499
- "%s %s is not a tool script, so its setter ran in the headless engine writing this as it runs "
500
- + "in the game; an editor runs none and would have saved the value given. "
509
+ "%s %s is not a tool script, so its setter ran in the headless engine writing this "
510
+ + "as it runs in the game; an editor runs none and would have saved the value "
511
+ + "given. "
501
512
  )
502
513
  % [refused, named]
503
514
  )
@@ -505,16 +516,58 @@ static func setter_ran(object: Object, property: String, refused: String) -> Str
505
516
  )
506
517
 
507
518
 
519
+ ## Moves out of alsoChanged in [param answer] each of [param names], what a headless write put back
520
+ ## as the doing of the script of [param owner], which is not a tool, to setByScript, with a note
521
+ ## saying why the file does not hold it.
522
+ static func move_script_set(answer: Dictionary, names: Array, owner: String) -> void:
523
+ if not answer.has("alsoChanged"):
524
+ return
525
+ var changed: Dictionary = answer["alsoChanged"]
526
+ var by_script: Dictionary = {}
527
+ for key: Variant in names:
528
+ if changed.has(key):
529
+ by_script[key] = changed[key]
530
+ var _erased: bool = changed.erase(key)
531
+ if by_script.is_empty():
532
+ return
533
+ if changed.is_empty():
534
+ var _dropped: bool = answer.erase("alsoChanged")
535
+ answer["setByScript"] = by_script
536
+ var listed: Array = by_script.keys()
537
+ var pronoun: String = "it" if listed.size() == 1 else "them"
538
+ answer["setByScriptNote"] = (
539
+ (
540
+ "%s's script is not a tool, so it ran in the headless engine that wrote the file, and "
541
+ + "set %s as it did. The file does not hold %s, as a save from the editor, where the "
542
+ + "script does not run, would not; the script sets %s again when it runs in the game."
543
+ )
544
+ % [owner, ", ".join(listed), pronoun, pronoun]
545
+ )
546
+
547
+
508
548
  ## Why [param name] on [param what] cannot be saved as the file held it.
509
549
  static func unasked(what: String, name: String, why: String = "") -> String:
510
550
  return (
511
551
  (
512
552
  (
513
- "%s.%s would be saved differently from what the file holds, and the call did not ask for "
514
- + "it: its script ran when a headless engine loaded or set it, as it does in the game and not "
515
- + "in an editor%s. "
553
+ "%s.%s would be saved differently from what the file holds, and the call did not "
554
+ + "ask for it: its script ran when a headless engine loaded or set it, as it does "
555
+ + "in the game and not in an editor%s. "
516
556
  )
517
557
  % [what, name, "" if why.is_empty() else "; " + why.trim_suffix(".")]
518
558
  )
519
559
  + REMEDY
520
560
  )
561
+
562
+
563
+ ## A file just saved: where it is, the text it held before, and [member resave], a Callable that
564
+ ## saves it again and answers an Error.
565
+ class GdhSaved:
566
+ var path: String
567
+ var before: String
568
+ var resave: Callable
569
+
570
+ func _init(at: String, held: String, again: Callable) -> void:
571
+ path = at
572
+ before = held
573
+ resave = again
@@ -1,13 +1,11 @@
1
1
  @tool
2
2
  extends EditorPlugin
3
-
4
3
  ## The editor half of gdharness: a client that holds a socket to the server and an executor
5
4
  ## that does what the server asks, with a label in the toolbar saying whether the two are
6
5
  ## connected.
7
6
 
8
7
  const GdhBridgeClient = preload("bridge_client.gd")
9
8
  const GdhToolExecutor = preload("tool_executor.gd")
10
-
11
9
  ## The tools answered while the editor scans or imports, because none of them saves a file: the
12
10
  ## ones a caller waits on a scan with, and the ones that only read or stop a played game.
13
11
  const ANSWERED_WHILE_SCANNING: Array[String] = [
@@ -102,7 +100,8 @@ func _on_disconnected() -> void:
102
100
  func turned_away(tool_name: String) -> bool:
103
101
  var filesystem: EditorFileSystem = EditorInterface.get_resource_filesystem()
104
102
  return (
105
- not ANSWERED_WHILE_SCANNING.has(tool_name) and (filesystem.is_scanning() or filesystem.is_importing())
103
+ not ANSWERED_WHILE_SCANNING.has(tool_name)
104
+ and (filesystem.is_scanning() or filesystem.is_importing())
106
105
  )
107
106
 
108
107
 
@@ -1,11 +1,9 @@
1
1
  @tool
2
2
  extends RefCounted
3
-
4
3
  ## Where a property a tool writes lives: on the object given, on a resource built into it, or on an
5
4
  ## element of one of its lists that the write has to make first.
6
5
 
7
6
  const GdhRead = preload("reading.gd")
8
-
9
7
  const HEADINGS: int = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP
10
8
 
11
9
 
@@ -95,11 +93,17 @@ static func _unlisted_in(items: Array, called: String, name: String, at: int) ->
95
93
  var end: int = name.find("/", at + 1)
96
94
  var index: String = name.substr(at + 1) if end < 0 else name.substr(at + 1, end - at - 1)
97
95
  if items.is_empty():
98
- return "%s holds no elements, so %s names none: give %s a whole list first." % [called, name, head]
96
+ return (
97
+ "%s holds no elements, so %s names none: give %s a whole list first."
98
+ % [called, name, head]
99
+ )
99
100
  var last: int = items.size() - 1
100
101
  if not index.is_valid_int():
101
102
  return (
102
- "%s is a list, so the step after it is the index of one of its elements, from 0 to %d, not %s."
103
+ (
104
+ "%s is a list, so the step after it is the index of one of its elements, from 0 to "
105
+ + "%d, not %s."
106
+ )
103
107
  % [called, last, index]
104
108
  )
105
109
  var place: int = index.to_int()
@@ -116,8 +120,8 @@ static func _unlisted_in(items: Array, called: String, name: String, at: int) ->
116
120
  if end < 0:
117
121
  return (
118
122
  (
119
- "%s/%d is an element of %s, which a path does not replace alone: give %s a whole list, or "
120
- + "write a property of the element, as %s/%d/<property>."
123
+ "%s/%d is an element of %s, which a path does not replace alone: give %s a whole "
124
+ + "list, or write a property of the element, as %s/%d/<property>."
121
125
  )
122
126
  % [head, place, called, head, head, place]
123
127
  )
@@ -197,12 +201,15 @@ static func grow(target: Object, name: String, called: String) -> String:
197
201
  above if not above.is_empty() else called,
198
202
  count,
199
203
  element.replace("_", " "),
200
- "" if count == 1 else "s"
204
+ "" if count == 1 else "s",
201
205
  ]
202
206
  )
203
207
  if index > count:
204
208
  return (
205
- "%s, and a new one goes after the last, so %s%d cannot be written yet: write %s%d first."
209
+ (
210
+ "%s, and a new one goes after the last, so %s%d cannot be written yet: write "
211
+ + "%s%d first."
212
+ )
206
213
  % [held, prefix, index, prefix, count]
207
214
  )
208
215
  if not adding.is_valid():
@@ -263,7 +270,10 @@ static func held_elsewhere(target: Object, name: String, home: String) -> String
263
270
  var file: String = "" if held == null else held.resource_path.get_slice("::", 0)
264
271
  if held != null and not held.is_built_in():
265
272
  return (
266
- "%s.%s is %s, a file of its own, so %s is written there, with resource_edit modify on %s."
273
+ (
274
+ "%s.%s is %s, a file of its own, so %s is written there, with resource_edit "
275
+ + "modify on %s."
276
+ )
267
277
  % [class_called(target), head, file, rest, file]
268
278
  )
269
279
  if held != null and not file.is_empty() and not home.is_empty() and file != home:
@@ -287,9 +297,9 @@ static func _built_in_elsewhere(
287
297
  node = " on %s" % from.get_path_to(placed)
288
298
  var text: String = (
289
299
  (
290
- "%s.%s is built into %s, so %s is written there, with scene_node set%s in %s, which changes "
291
- + "it wherever that scene is used. To change it here alone, give %s a %s of its own: a "
292
- + 'value with "_type" set to its class and its properties beside it.'
300
+ "%s.%s is built into %s, so %s is written there, with scene_node set%s in %s, which "
301
+ + "changes it wherever that scene is used. To change it here alone, give %s a %s of "
302
+ + 'its own: a value with "_type" set to its class and its properties beside it.'
293
303
  )
294
304
  % [class_called(target), head, file, rest, node, file, head, held.get_class()]
295
305
  )