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,24 +1,10 @@
1
1
  @tool
2
2
  extends EditorPlugin
3
-
4
3
  ## Reloads the open scene and the scripts it carries when they change on disk, without the
5
4
  ## editor's confirmation popup. Every edit made through gdharness lands on disk, and an editor
6
5
  ## that keeps showing the old file is how a change reads as one that never happened. The
7
6
  ## files are polled once a second; the editor's own watcher only fires on window focus.
8
7
 
9
-
10
- ## What was last seen of a watched file.
11
- class GdhSeen:
12
- ## Its modification time, in whole seconds.
13
- var time: int
14
- ## The MD5 of its contents.
15
- var md5: String
16
-
17
- func _init(seen_time: int, seen_md5: String) -> void:
18
- time = seen_time
19
- md5 = seen_md5
20
-
21
-
22
8
  const CHECK_INTERVAL_SECONDS: float = 1.0
23
9
  ## How long after a file's modification time its contents are compared as well. The time is in
24
10
  ## whole seconds, so a second write inside the second of the first leaves it as it was and changes
@@ -135,15 +121,16 @@ func _reload_scene(path: String) -> void:
135
121
  var edited_scene: Node = EditorInterface.get_edited_scene_root()
136
122
  if not edited_scene or edited_scene.scene_file_path != path:
137
123
  return
138
- # A reload from the file drops the edits the editor holds, without a prompt. gdharness refuses to
139
- # write over them, so what is seen here is a write by somebody else or one the editor's tab has
140
- # already taken in, made just before an edit inside the same poll, and neither is worth the edit.
124
+ # A reload from the file drops the edits the editor holds, without a prompt. gdharness refuses
125
+ # to write over them, so what is seen here is a write by somebody else or one the editor's tab
126
+ # has already taken in, made just before an edit inside the same poll, and neither is worth the
127
+ # edit.
141
128
  if EditorInterface.get_unsaved_scenes().has(path):
142
129
  push_warning(
143
130
  (
144
131
  (
145
- "gdharness: %s changed on disk while the editor holds unsaved changes to it, so it "
146
- + "was not reloaded; saving it writes the editor's copy over the file"
132
+ "gdharness: %s changed on disk while the editor holds unsaved changes to it, "
133
+ + "so it was not reloaded; saving it writes the editor's copy over the file"
147
134
  )
148
135
  % path
149
136
  )
@@ -151,3 +138,15 @@ func _reload_scene(path: String) -> void:
151
138
  return
152
139
  print("[gdharness] Scene changed on disk, reloading: ", path)
153
140
  EditorInterface.reload_scene_from_path(path)
141
+
142
+
143
+ ## What was last seen of a watched file.
144
+ class GdhSeen:
145
+ ## Its modification time, in whole seconds.
146
+ var time: int
147
+ ## The MD5 of its contents.
148
+ var md5: String
149
+
150
+ func _init(seen_time: int, seen_md5: String) -> void:
151
+ time = seen_time
152
+ md5 = seen_md5
@@ -1,13 +1,11 @@
1
1
  extends RefCounted
2
-
3
- ## Animations and state machines read off their nodes: by the editor's tools from the scene it holds,
4
- ## and by a headless engine with none connected from a bare node of the class given what the file
5
- ## stores, which runs no script of the scene's, so a read answers the same either way.
2
+ ## Animations and state machines read off their nodes: by the editor's tools from the scene it
3
+ ## holds, and by a headless engine with none connected from a bare node of the class given what the
4
+ ## file stores, which runs no script of the scene's, so a read answers the same either way.
6
5
 
7
6
  const GdhFileReads = preload("file_reads.gd")
8
7
  const GdhRead = preload("reading.gd")
9
8
  const GdhSerialisation = preload("serialisation.gd")
10
-
11
9
  const LOOP_MODE_NAMES: Dictionary[int, String] = {
12
10
  Animation.LOOP_NONE: "none",
13
11
  Animation.LOOP_LINEAR: "linear",
@@ -37,7 +35,9 @@ const ADVANCE_MODE_NAMES: Dictionary[int, String] = {
37
35
 
38
36
 
39
37
  ## [method animations] of the AnimationPlayer at [param player_path] in the file [param scene_path].
40
- static func scene_animations(scene_path: String, player_path: String, animation_name: String) -> Dictionary:
38
+ static func scene_animations(
39
+ scene_path: String, player_path: String, animation_name: String
40
+ ) -> Dictionary:
41
41
  var made: Array = _bare(scene_path, player_path, "AnimationPlayer")
42
42
  if made[0] == null:
43
43
  return made[1]
@@ -48,7 +48,9 @@ static func scene_animations(scene_path: String, player_path: String, animation_
48
48
 
49
49
 
50
50
  ## [method states] of the AnimationTree at [param tree_path] in the file [param scene_path].
51
- static func scene_states(scene_path: String, tree_path: String, state_machine_path: String) -> Dictionary:
51
+ static func scene_states(
52
+ scene_path: String, tree_path: String, state_machine_path: String
53
+ ) -> Dictionary:
52
54
  var made: Array = _bare(scene_path, tree_path, "AnimationTree")
53
55
  if made[0] == null:
54
56
  return made[1]
@@ -104,9 +106,9 @@ static func animations(player: AnimationPlayer, animation_name: String) -> Dicti
104
106
  summary["animationName"] = called
105
107
  summary["tracks"] = listed_one.get_track_count()
106
108
  listed.append(summary)
107
- return {"ok": true, "animations": listed}
109
+ return { "ok": true, "animations": listed }
108
110
  if not player.has_animation(StringName(animation_name)):
109
- return {"ok": false, "error": no_animation(player, str(player.name), animation_name)}
111
+ return { "ok": false, "error": no_animation(player, str(player.name), animation_name) }
110
112
  var anim: Animation = player.get_animation(StringName(animation_name))
111
113
  var answer: Dictionary = _summary(anim)
112
114
  answer["ok"] = true
@@ -158,7 +160,8 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
158
160
  var kind: int = anim.track_get_type(track)
159
161
  var path: NodePath = anim.track_get_path(track)
160
162
  var shaped: Dictionary = {
161
- "type": TRACK_TYPE_NAMES.get(kind, str(kind)), "nodePath": str(path.get_concatenated_names())
163
+ "type": TRACK_TYPE_NAMES.get(kind, str(kind)),
164
+ "nodePath": str(path.get_concatenated_names()),
162
165
  }
163
166
  var property: String = str(path.get_concatenated_subnames())
164
167
  if not property.is_empty():
@@ -166,7 +169,7 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
166
169
  var keyframes: Array[Dictionary] = []
167
170
  var methods: Dictionary = {}
168
171
  for key: int in anim.track_get_key_count(track):
169
- var keyframe: Dictionary = {"time": held_seconds(anim.track_get_key_time(track, key))}
172
+ var keyframe: Dictionary = { "time": held_seconds(anim.track_get_key_time(track, key)) }
170
173
  if kind == Animation.TYPE_METHOD:
171
174
  var method: String = str(anim.method_track_get_name(track, key))
172
175
  methods[method] = true
@@ -188,7 +191,9 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
188
191
  ## The state machine at a slash-separated path of nested state machine names in [param tree], or
189
192
  ## null when any step of the path is not one. Every node in a tree of state machines is a state
190
193
  ## machine itself, so the walk asks each step for the child by name and refuses anything else.
191
- static func state_machine(tree: AnimationTree, state_machine_path: String) -> AnimationNodeStateMachine:
194
+ static func state_machine(
195
+ tree: AnimationTree, state_machine_path: String
196
+ ) -> AnimationNodeStateMachine:
192
197
  if tree == null:
193
198
  return null
194
199
  var current: AnimationNodeStateMachine = tree.tree_root as AnimationNodeStateMachine
@@ -207,8 +212,8 @@ static func no_machine(tree: AnimationTree, state_machine_path: String) -> Strin
207
212
  if tree == null or tree.tree_root == null:
208
213
  return (
209
214
  "The AnimationTree has no tree_root. Give it one with scene_node set: tree_root "
210
- + '{"_type": "AnimationNodeStateMachine"} builds one into the scene, and the res:// path '
211
- + "of one made with resource_edit create keeps it in a file of its own."
215
+ + '{"_type": "AnimationNodeStateMachine"} builds one into the scene, and the res:// '
216
+ + "path of one made with resource_edit create keeps it in a file of its own."
212
217
  )
213
218
  var current: AnimationNodeStateMachine = tree.tree_root as AnimationNodeStateMachine
214
219
  if current == null:
@@ -242,8 +247,8 @@ static func no_machine(tree: AnimationTree, state_machine_path: String) -> Strin
242
247
  return "No state machine at " + state_machine_path
243
248
 
244
249
 
245
- ## Why no state finds its animation when [param anim_player], an AnimationTree's path to its
246
- ## player, reaches no AnimationPlayer: [param reached] is the node it reaches instead, null for none.
250
+ ## Why no state finds its animation when [param anim_player], an AnimationTree's path to its player,
251
+ ## reaches no AnimationPlayer: [param reached] is the node it reaches instead, null for none.
247
252
  static func no_player(anim_player: String, reached: Node) -> String:
248
253
  var until: String = "so no state plays anything until scene_node set points anim_player at one."
249
254
  if anim_player.is_empty():
@@ -259,16 +264,25 @@ static func no_player(anim_player: String, reached: Node) -> String:
259
264
  ## Why a state playing [param animation_name] from [param player], at [param player_path] from the
260
265
  ## scene root as playerNodePath takes it, plays nothing yet. anim_player is relative to the tree
261
266
  ## ("../Anim"), which no argument takes.
262
- static func missing_animation(player: AnimationPlayer, player_path: String, animation_name: String) -> String:
267
+ static func missing_animation(
268
+ player: AnimationPlayer, player_path: String, animation_name: String
269
+ ) -> String:
263
270
  var said: String = (
264
- "The AnimationPlayer at %s has no animation %s yet, so the state plays nothing until it has one."
271
+ (
272
+ "The AnimationPlayer at %s has no animation %s yet, so the state plays nothing until "
273
+ + "it has one."
274
+ )
265
275
  % [player_path, animation_name]
266
276
  )
267
277
  var close: PackedStringArray = GdhRead.nearest(animation_name, player.get_animation_list())
268
278
  if close.is_empty():
269
279
  return said
270
280
  return (
271
- said + " The nearest %s %s." % ["it has is" if close.size() == 1 else "it has are", ", ".join(close)]
281
+ said
282
+ + (
283
+ " The nearest %s %s."
284
+ % ["it has is" if close.size() == 1 else "it has are", ", ".join(close)]
285
+ )
272
286
  )
273
287
 
274
288
 
@@ -285,11 +299,11 @@ static func states(
285
299
  ) -> Dictionary:
286
300
  var sm: AnimationNodeStateMachine = state_machine(tree, state_machine_path)
287
301
  if sm == null:
288
- return {"ok": false, "error": no_machine(tree, state_machine_path)}
302
+ return { "ok": false, "error": no_machine(tree, state_machine_path) }
289
303
  var listed: Array[Dictionary] = []
290
304
  for state: StringName in sm.get_node_list():
291
305
  var node: AnimationNode = sm.get_node(state)
292
- var entry: Dictionary = {"stateName": str(state), "type": node.get_class()}
306
+ var entry: Dictionary = { "stateName": str(state), "type": node.get_class() }
293
307
  if node is AnimationNodeAnimation:
294
308
  var plays: AnimationNodeAnimation = node
295
309
  entry["animationName"] = str(plays.animation)
@@ -298,7 +312,9 @@ static func states(
298
312
  if player == null:
299
313
  entry["animationMissing"] = unplayed
300
314
  elif not found:
301
- entry["animationMissing"] = missing_animation(player, player_path, str(plays.animation))
315
+ entry["animationMissing"] = missing_animation(
316
+ player, player_path, str(plays.animation)
317
+ )
302
318
  listed.append(entry)
303
319
  var transitions: Array[Dictionary] = []
304
320
  for index: int in sm.get_transition_count():
@@ -306,8 +322,10 @@ static func states(
306
322
  var shaped: Dictionary = {
307
323
  "from": str(sm.get_transition_from(index)),
308
324
  "to": str(sm.get_transition_to(index)),
309
- "transitionType": SWITCH_MODE_NAMES.get(transition.switch_mode, str(transition.switch_mode)),
310
- "advanceMode": ADVANCE_MODE_NAMES.get(transition.advance_mode, str(transition.advance_mode)),
325
+ "transitionType":
326
+ SWITCH_MODE_NAMES.get(transition.switch_mode, str(transition.switch_mode)),
327
+ "advanceMode":
328
+ ADVANCE_MODE_NAMES.get(transition.advance_mode, str(transition.advance_mode)),
311
329
  }
312
330
  var condition: String = str(transition.advance_condition)
313
331
  if not condition.is_empty():
@@ -317,9 +335,9 @@ static func states(
317
335
  if not condition.is_empty() and transition.advance_mode != transition.ADVANCE_MODE_AUTO:
318
336
  shaped["conditionNote"] = (
319
337
  (
320
- "advanceMode is %s, so %s never advances it: the engine checks a condition only on an"
321
- + " auto transition%s. disconnect_states and connect_states again with the condition"
322
- + " write it as auto"
338
+ "advanceMode is %s, so %s never advances it: the engine checks a condition "
339
+ + "only on an auto transition%s. disconnect_states and connect_states again "
340
+ + "with the condition write it as auto"
323
341
  )
324
342
  % [
325
343
  shaped["advanceMode"],
@@ -328,16 +346,16 @@ static func states(
328
346
  ", and travel() still takes this one"
329
347
  if transition.advance_mode == transition.ADVANCE_MODE_ENABLED
330
348
  else ""
331
- )
349
+ ),
332
350
  ]
333
351
  )
334
352
  transitions.append(shaped)
335
- return {"ok": true, "states": listed, "transitions": transitions}
353
+ return { "ok": true, "states": listed, "transitions": transitions }
336
354
 
337
355
 
338
356
  ## The states of the AnimationTrees under [param root] that play [param animation_name] from
339
- ## [param player], as [[node, {animTreePath, state}]], a state named by the path of the nodes leading
340
- ## to it.
357
+ ## [param player], as [[node, {animTreePath, state}]], a state named by the path of the nodes
358
+ ## leading to it.
341
359
  static func states_playing(root: Node, player: AnimationPlayer, animation_name: String) -> Array:
342
360
  var found: Array = []
343
361
  var trees: Array[Node] = root.find_children("*", "AnimationTree", true, false)
@@ -358,7 +376,9 @@ static func _collect_playing(
358
376
  if node is AnimationNodeAnimation:
359
377
  var plays: AnimationNodeAnimation = node
360
378
  if plays.animation == StringName(animation_name):
361
- found.append([plays, {"animTreePath": tree_path, "state": "." if at.is_empty() else at}])
379
+ found.append(
380
+ [plays, { "animTreePath": tree_path, "state": "." if at.is_empty() else at }]
381
+ )
362
382
  return
363
383
  var children: Dictionary = {}
364
384
  if node is AnimationNodeStateMachine:
@@ -379,4 +399,6 @@ static func _collect_playing(
379
399
  children[str(point)] = plane.get_blend_point_node(point)
380
400
  for step: String in children:
381
401
  var child: AnimationNode = children[step]
382
- _collect_playing(child, step if at.is_empty() else at + "/" + step, tree_path, animation_name, found)
402
+ _collect_playing(
403
+ child, step if at.is_empty() else at + "/" + step, tree_path, animation_name, found
404
+ )
@@ -1,6 +1,5 @@
1
1
  @tool
2
2
  extends Node
3
-
4
3
  ## The editor's end of the bridge: one WebSocket to the gdharness server, reconnected on its
5
4
  ## own when it drops, carrying tool requests in and their results out.
6
5
 
@@ -9,7 +8,6 @@ signal disconnected
9
8
  signal tool_requested(request_id: String, tool_name: String, args: Dictionary)
10
9
 
11
10
  const GdhRead = preload("reading.gd")
12
-
13
11
  const DEFAULT_URL: String = "ws://127.0.0.1:6505/godot"
14
12
  ## Written beside the addon by the install, so it names the version this copy came from.
15
13
  const VERSION_MARKER: String = "res://addons/gdharness_editor/.gdharness-version"
@@ -24,7 +22,6 @@ const EDITORS_RECORDED: String = "res://.godot/gdharness-editors"
24
22
  const EDITORS_RECORDED_PROTOCOL: int = 1
25
23
  const RECONNECT_DELAY: float = 3.0
26
24
  const MAX_RECONNECT_DELAY: float = 30.0
27
-
28
25
  ## What a server closes with when the editor that said hello belongs to another project. Kept in
29
26
  ## step with `OTHER_PROJECT_CLOSE_CODE` in src/godot-bridge.ts.
30
27
  const ELSEWHERE_CLOSE_CODE: int = 4001
@@ -32,7 +29,6 @@ const ELSEWHERE_CLOSE_CODE: int = 4001
32
29
  ## rather than an editor: dialling in again changes nothing for the rest of the run. Kept in step
33
30
  ## with `ONE_SHOT_CLOSE_CODE` in src/godot-bridge.ts.
34
31
  const ONE_SHOT_CLOSE_CODE: int = 4002
35
-
36
32
  ## Where Godot keeps the three ports an editor serves. None of them is per editor: the settings
37
33
  ## file is one for every editor of this engine version on the machine, so two open at once want the
38
34
  ## same three and the second one binds nothing. Read here and never written, for the reason
@@ -58,7 +54,6 @@ const OPENED_BY_A_SERVER: String = "GDHARNESS_OPENED_BY_A_SERVER"
58
54
  ## tells the two apart when both are announced at once. Kept in step with `EDITOR_PID_VARIABLE`
59
55
  ## in the runtime autoload.
60
56
  const EDITOR_PID_VARIABLE: String = "GDHARNESS_EDITOR_PID"
61
-
62
57
  ## How long one attempt is given before the address is called a bad one.
63
58
  ##
64
59
  ## A socket pointed at a port nothing holds gives up by itself, in thirty seconds on Windows. One
@@ -67,7 +62,6 @@ const EDITOR_PID_VARIABLE: String = "GDHARNESS_EDITOR_PID"
67
62
  ## timeout of its own in 4.7. A leftover announcement can be either, and a port that has been
68
63
  ## reused by some other program is the second.
69
64
  const CONNECT_TIMEOUT: float = 10.0
70
-
71
65
  ## How often the announcement is read again while connected, so a newer server is moved to
72
66
  ## rather than waited for. A harness reconnect leaves the server it replaced running and holding
73
67
  ## the old port, and an editor with no reason to look elsewhere stayed on it for the session.
@@ -75,13 +69,11 @@ const FOLLOW_INTERVAL: float = 5.0
75
69
 
76
70
  var socket: WebSocketPeer = WebSocketPeer.new()
77
71
  var server_url: String = DEFAULT_URL
78
-
79
72
  ## What this copy was when it loaded, which is what the editor is running until it is restarted.
80
73
  ## See [method _loaded_version] for why it is held rather than read when it is wanted.
81
74
  var version_at_load: String = ""
82
75
  ## The digest of the editor addon code this copy loaded, held for the same reason.
83
76
  var digest_at_load: String = ""
84
-
85
77
  var _is_connected: bool = false
86
78
  var _reconnect_timer: Timer
87
79
  var _current_reconnect_delay: float = RECONNECT_DELAY
@@ -89,27 +81,23 @@ var _current_reconnect_delay: float = RECONNECT_DELAY
89
81
  var _should_reconnect: bool = false
90
82
  var _project_path: String
91
83
  var _initialized: bool = false
92
-
93
84
  ## Whether the address came from a caller rather than from the project, in which case it is not
94
85
  ## this node's to change.
95
86
  var _named_by_caller: bool = false
96
87
  var _since_looked: float = 0.0
97
-
98
88
  ## The announced address that did not answer, so the fallback gets a turn. Cleared the moment
99
89
  ## anything connects or the project names a different one.
100
90
  var _refused_url: String = ""
101
91
  var _tried_announced: bool = false
102
-
103
92
  ## The address this editor has already been told is another project's, so a reconnect loop says it
104
93
  ## once rather than every few seconds for the rest of the session.
105
94
  var _told_about_url: String = ""
106
95
  var _connecting_for: float = 0.0
107
-
108
96
  ## Whether this editor was started by another editor, which is Godot's own restart and the project
109
97
  ## manager opening a project: the new editor is the old one's child and has its environment, with
110
98
  ## the variable this addon sets as it loads still naming the old one. Its arguments are gone, since
111
- ## the engine consumes `--lsp-port`, `--dap-port` and `--log-file` and hands none of them back, so the
112
- ## ports and the marker a server put in that environment describe the editor before this one.
99
+ ## the engine consumes `--lsp-port`, `--dap-port` and `--log-file` and hands none of them back, so
100
+ ## the ports and the marker a server put in that environment describe the editor before this one.
113
101
  var _started_by_an_editor: bool = false
114
102
 
115
103
 
@@ -140,45 +128,6 @@ func _exit_tree() -> void:
140
128
  var _removed: Error = DirAccess.remove_absolute(record)
141
129
 
142
130
 
143
- ## The file saying which ports this editor serves, named by its process id, so `gdharness lsp` can
144
- ## find its language server when a server moved it off the default. One for each process rather
145
- ## than one for the project, because a gate's `--import` loads this addon too, and its plugin is
146
- ## shown none of the options that would say so; the reader tells the two apart by command line.
147
- ## Kept in step with `editorRecordsIn` in src/lsp-bridge.ts.
148
- static func _ports_record() -> String:
149
- return ProjectSettings.globalize_path(EDITORS_RECORDED).path_join("%d.json" % OS.get_process_id())
150
-
151
-
152
- func _record_ports() -> void:
153
- if not Engine.is_editor_hint():
154
- return
155
- var made: Error = DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(EDITORS_RECORDED))
156
- if made != OK and made != ERR_ALREADY_EXISTS:
157
- return
158
- var file: FileAccess = FileAccess.open(_ports_record(), FileAccess.WRITE)
159
- if file == null:
160
- return
161
- var written: bool = (
162
- file
163
- . store_string(
164
- (
165
- JSON
166
- . stringify(
167
- {
168
- "protocol": EDITORS_RECORDED_PROTOCOL,
169
- "pid": OS.get_process_id(),
170
- "lsp_port": _serves(LSP_ASKED, LSP_SETTING),
171
- "dap_port": _serves(DAP_ASKED, DAP_SETTING),
172
- "written_at_ms": int(Time.get_unix_time_from_system() * 1000.0),
173
- }
174
- )
175
- )
176
- )
177
- )
178
- if not written:
179
- push_warning("gdharness: could not write " + _ports_record())
180
-
181
-
182
131
  func _process(_delta: float) -> void:
183
132
  if not _initialized:
184
133
  return
@@ -220,6 +169,49 @@ func _process(_delta: float) -> void:
220
169
  _handle_disconnect()
221
170
 
222
171
 
172
+ ## The file saying which ports this editor serves, named by its process id, so `gdharness lsp` can
173
+ ## find its language server when a server moved it off the default. One for each process rather
174
+ ## than one for the project, because a gate's `--import` loads this addon too, and its plugin is
175
+ ## shown none of the options that would say so; the reader tells the two apart by command line.
176
+ ## Kept in step with `editorRecordsIn` in src/lsp-bridge.ts.
177
+ static func _ports_record() -> String:
178
+ return ProjectSettings.globalize_path(EDITORS_RECORDED).path_join(
179
+ "%d.json" % OS.get_process_id()
180
+ )
181
+
182
+
183
+ func _record_ports() -> void:
184
+ if not Engine.is_editor_hint():
185
+ return
186
+ var made: Error = DirAccess.make_dir_recursive_absolute(
187
+ ProjectSettings.globalize_path(EDITORS_RECORDED)
188
+ )
189
+ if made != OK and made != ERR_ALREADY_EXISTS:
190
+ return
191
+ var file: FileAccess = FileAccess.open(_ports_record(), FileAccess.WRITE)
192
+ if file == null:
193
+ return
194
+ var written: bool = (
195
+ file
196
+ . store_string(
197
+ (
198
+ JSON
199
+ . stringify(
200
+ {
201
+ "protocol": EDITORS_RECORDED_PROTOCOL,
202
+ "pid": OS.get_process_id(),
203
+ "lsp_port": _serves(LSP_ASKED, LSP_SETTING),
204
+ "dap_port": _serves(DAP_ASKED, DAP_SETTING),
205
+ "written_at_ms": int(Time.get_unix_time_from_system() * 1000.0),
206
+ }
207
+ )
208
+ )
209
+ )
210
+ )
211
+ if not written:
212
+ push_warning("gdharness: could not write " + _ports_record())
213
+
214
+
223
215
  ## Moves to the server the project now names, when that is not the one this is talking to.
224
216
  ##
225
217
  ## A harness reconnect leaves the server it replaced running, still holding the port it bound and
@@ -371,7 +363,7 @@ func _handle_connect() -> void:
371
363
  # as itself: the engine hands back none of the arguments it was given, so the arguments
372
364
  # have to come from whoever wrote them. See `restart_editor` in play_tools.gd.
373
365
  "opened_by_a_server": _opened_by_a_server() and not _started_by_an_editor,
374
- "started_by_an_editor": _started_by_an_editor
366
+ "started_by_an_editor": _started_by_an_editor,
375
367
  }
376
368
  )
377
369
 
@@ -556,7 +548,7 @@ func _handle_message(json_string: String) -> void:
556
548
 
557
549
  match message.get("type", ""):
558
550
  "ping":
559
- _send_message({"type": "pong"})
551
+ _send_message({ "type": "pong" })
560
552
 
561
553
  "tool_invoke":
562
554
  var request_id: String = message.get("id", "")
@@ -577,13 +569,16 @@ func send_busy(request_id: String, tool_name: String) -> void:
577
569
  "id": request_id,
578
570
  "success": false,
579
571
  "busy": true,
580
- "error": "The editor is scanning or importing its files, so %s was not run." % tool_name,
572
+ "error":
573
+ "The editor is scanning or importing its files, so %s was not run." % tool_name,
581
574
  }
582
575
  )
583
576
 
584
577
 
585
- func send_tool_result(request_id: String, success: bool, result: Variant = null, error: String = "") -> void:
586
- var response: Dictionary = {"type": "tool_result", "id": request_id, "success": success}
578
+ func send_tool_result(
579
+ request_id: String, success: bool, result: Variant = null, error: String = ""
580
+ ) -> void:
581
+ var response: Dictionary = { "type": "tool_result", "id": request_id, "success": success }
587
582
 
588
583
  if success:
589
584
  response["result"] = result