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.
- package/build/cli.js +383 -243
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +55 -33
- package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
- package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_editor/file_reads.gd +83 -42
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +68 -44
- package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
- package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
- package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
- package/build/godot/addons/gdharness_editor/reading.gd +51 -17
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +127 -80
- package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +37 -27
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +79 -49
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +65 -42
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +166 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +68 -41
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +12 -5
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +113 -67
- package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
- package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
- package/build/godot/addons/gdharness_runtime/reading.gd +51 -17
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +149 -147
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +34 -17
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd +15 -15
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_tests/bench.gd +4 -5
- package/build/godot/addons/gdharness_tests/event.gd +10 -5
- package/build/godot/addons/gdharness_tests/share.gd +38 -18
- package/build/godot/addons/gdharness_tests/suites.gd +3 -3
- package/build/godot/addons/gdharness_tests/wait.gd +3 -2
- package/build/godot/loader/loader.gd +9 -9
- package/build/godot/operations/animation_reads.gd +55 -33
- package/build/godot/operations/audio_buses.gd +48 -19
- package/build/godot/operations/class_cache.gd +16 -11
- package/build/godot/operations/classdb_queries.gd +89 -44
- package/build/godot/operations/declared_values.gd +68 -32
- package/build/godot/operations/dependencies.gd +190 -109
- package/build/godot/operations/editor_tools.gd +10 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +83 -42
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +86 -65
- package/build/godot/operations/gdscript_authoring.gd +83 -36
- package/build/godot/operations/godot_operations.gd +17 -11
- package/build/godot/operations/import_pipeline.gd +110 -59
- package/build/godot/operations/input_actions.gd +62 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +61 -32
- package/build/godot/operations/project_diagnostics.gd +24 -11
- package/build/godot/operations/project_file.gd +16 -16
- package/build/godot/operations/project_validation.gd +71 -44
- package/build/godot/operations/property_paths.gd +22 -12
- package/build/godot/operations/property_values.gd +143 -78
- package/build/godot/operations/reading.gd +51 -17
- package/build/godot/operations/rename_checks.gd +2 -2
- package/build/godot/operations/resource_files.gd +11 -9
- package/build/godot/operations/same_values.gd +4 -4
- package/build/godot/operations/scene_connections.gd +27 -13
- package/build/godot/operations/script_check.gd +52 -49
- package/build/godot/operations/serialisation.gd +75 -51
- package/build/godot/operations/setting_reads.gd +7 -6
- package/build/godot/operations/value_shapes.gd +5 -6
- package/build/godot/tests/listed.gd +321 -268
- package/build/keeper.js +43 -43
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## Scenes and resources read from their files, by the editor's tools and by a headless engine with
|
|
4
3
|
## none connected, so a read answers the same either way.
|
|
5
4
|
##
|
|
@@ -30,7 +29,10 @@ static func scene_tree(
|
|
|
30
29
|
if not laid.get("ok", false):
|
|
31
30
|
return laid
|
|
32
31
|
var nodes: Dictionary = laid["nodes"]
|
|
33
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
"ok": true,
|
|
34
|
+
"tree": _entry(nodes, ".", include_properties, include_defaults, depth, 0),
|
|
35
|
+
}
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
## The node at [param node_path] in [param scene_path] as {ok, name, type, properties}, laid as
|
|
@@ -45,7 +47,9 @@ static func scene_node(scene_path: String, node_path: String) -> Dictionary:
|
|
|
45
47
|
if not nodes.has(path):
|
|
46
48
|
return {}
|
|
47
49
|
var found: Dictionary = nodes[path]
|
|
48
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
"ok": true, "name": found["name"], "type": found["type"], "properties": found["properties"],
|
|
52
|
+
}
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
## The node at [param node_path] in [param scene_path] as [method scene_node] answers it, when it is
|
|
@@ -67,7 +71,7 @@ static func scene_node_of_kind(scene_path: String, node_path: String, kind: Stri
|
|
|
67
71
|
"ok": true,
|
|
68
72
|
"name": nodes[path]["name"],
|
|
69
73
|
"type": found,
|
|
70
|
-
"properties": nodes[path]["properties"]
|
|
74
|
+
"properties": nodes[path]["properties"],
|
|
71
75
|
}
|
|
72
76
|
else:
|
|
73
77
|
var hinted: String = said
|
|
@@ -79,7 +83,7 @@ static func scene_node_of_kind(scene_path: String, node_path: String, kind: Stri
|
|
|
79
83
|
var type: String = str(nodes[at]["type"])
|
|
80
84
|
if ClassDB.class_exists(type) and ClassDB.is_parent_class(type, kind):
|
|
81
85
|
var _held: bool = held.append(str(at))
|
|
82
|
-
return {"ok": false, "error": GdhRead.not_of_kind(said, node_path, found, kind, held)}
|
|
86
|
+
return { "ok": false, "error": GdhRead.not_of_kind(said, node_path, found, kind, held) }
|
|
83
87
|
|
|
84
88
|
|
|
85
89
|
## [param said], the refusal for [param node_path], which the scene laid as [param nodes] does not
|
|
@@ -100,35 +104,38 @@ static func _missing_in(nodes: Dictionary, said: String, node_path: String) -> S
|
|
|
100
104
|
var children: Array[String] = nodes[at]["children"]
|
|
101
105
|
for child: String in children:
|
|
102
106
|
var _named: bool = names.append(str(nodes[child]["name"]))
|
|
103
|
-
return GdhRead.missing_below(
|
|
107
|
+
return GdhRead.missing_below(
|
|
108
|
+
said, str(nodes["."]["name"]), reached, parts[reached.size()], names
|
|
109
|
+
)
|
|
104
110
|
|
|
105
111
|
|
|
106
112
|
## Every node of [param scene_path] as {ok, nodes}, keyed by path, or a refusal.
|
|
107
113
|
static func _laid(scene_path: String) -> Dictionary:
|
|
108
114
|
if not FileAccess.file_exists(scene_path):
|
|
109
|
-
return {"ok": false, "error": "Scene not found: " + scene_path}
|
|
115
|
+
return { "ok": false, "error": "Scene not found: " + scene_path }
|
|
110
116
|
# Loaded without naming the type, so a resource handed in by mistake loads as what it is and the
|
|
111
117
|
# refusal can say so, where asking for a PackedScene printed the loader's error and a backtrace.
|
|
112
118
|
var loaded: Resource = ResourceLoader.load(scene_path, "", ResourceLoader.CACHE_MODE_IGNORE)
|
|
113
119
|
if loaded == null:
|
|
114
|
-
return {"ok": false, "error": "Failed to load %s as a scene" % scene_path}
|
|
120
|
+
return { "ok": false, "error": "Failed to load %s as a scene" % scene_path }
|
|
115
121
|
if not loaded is PackedScene:
|
|
116
122
|
var sort: String = GdhRead.with_article(loaded.get_class())
|
|
117
123
|
return {
|
|
118
124
|
"ok": false,
|
|
119
|
-
"error":
|
|
125
|
+
"error":
|
|
126
|
+
"%s is %s, not a scene: resource_edit read reads its properties." % [scene_path, sort],
|
|
120
127
|
}
|
|
121
128
|
var packed: PackedScene = loaded
|
|
122
129
|
var nodes: Dictionary = {}
|
|
123
130
|
var refused: String = _lay(packed.get_state(), ".", nodes, [scene_path], packed)
|
|
124
131
|
if not refused.is_empty():
|
|
125
|
-
return {"ok": false, "error": refused}
|
|
132
|
+
return { "ok": false, "error": refused }
|
|
126
133
|
if not nodes.has("."):
|
|
127
|
-
return {"ok": false, "error": "%s holds no nodes" % scene_path}
|
|
134
|
+
return { "ok": false, "error": "%s holds no nodes" % scene_path }
|
|
128
135
|
var base: SceneState = packed.get_state().get_base_scene_state()
|
|
129
136
|
if base != null:
|
|
130
137
|
nodes["."]["inherits"] = base.get_path()
|
|
131
|
-
return {"ok": true, "nodes": nodes}
|
|
138
|
+
return { "ok": true, "nodes": nodes }
|
|
132
139
|
|
|
133
140
|
|
|
134
141
|
## Lays the nodes of [param state] into [param nodes] under [param prefix], keyed by their path
|
|
@@ -152,8 +159,8 @@ static func _lay(
|
|
|
152
159
|
var path: String = _joined(prefix, str(state.get_node_path(index)))
|
|
153
160
|
var recovered: bool = false
|
|
154
161
|
if not nodes.has(path) and index > 0 and _overrides(state, index) and ids[index] >= 0:
|
|
155
|
-
# The engine finds an entry whose node was renamed or moved in the scene it comes from
|
|
156
|
-
# the node's unique_id, and keeps the node's new name.
|
|
162
|
+
# The engine finds an entry whose node was renamed or moved in the scene it comes from
|
|
163
|
+
# by the node's unique_id, and keeps the node's new name.
|
|
157
164
|
var parent: String = _joined(prefix, str(state.get_node_path(index, true)))
|
|
158
165
|
var found: String = _by_id(nodes, _owner_of(nodes, parent, prefix), ids[index])
|
|
159
166
|
recovered = not found.is_empty()
|
|
@@ -164,7 +171,9 @@ static func _lay(
|
|
|
164
171
|
var instance: PackedScene = state.get_node_instance(index)
|
|
165
172
|
if instance != null:
|
|
166
173
|
if within.has(instance.resource_path):
|
|
167
|
-
return
|
|
174
|
+
return (
|
|
175
|
+
"%s instances %s, which holds it" % [within.back(), instance.resource_path]
|
|
176
|
+
)
|
|
168
177
|
var refused: String = _lay(
|
|
169
178
|
instance.get_state(), path, nodes, within + [instance.resource_path]
|
|
170
179
|
)
|
|
@@ -178,15 +187,16 @@ static func _lay(
|
|
|
178
187
|
nodes[path] = _made("InstancePlaceholder")
|
|
179
188
|
nodes[path]["instanceOf"] = state.get_node_instance_placeholder(index)
|
|
180
189
|
elif str(state.get_node_type(index)).is_empty():
|
|
181
|
-
# An entry for a node of an instance that no longer has it, which the engine skips
|
|
190
|
+
# An entry for a node of an instance that no longer has it, which the engine skips
|
|
191
|
+
# too.
|
|
182
192
|
continue
|
|
183
193
|
else:
|
|
184
194
|
nodes[path] = _made(str(state.get_node_type(index)))
|
|
185
195
|
nodes[path]["madeHere"] = loaded != null
|
|
186
196
|
if ids[index] >= 0:
|
|
187
197
|
nodes[path]["uniqueId"] = ids[index]
|
|
188
|
-
# Laid at the root with no scene handed in is a scene the one being read inherits, and
|
|
189
|
-
# deepest is laid first, so this is the file that makes the node.
|
|
198
|
+
# Laid at the root with no scene handed in is a scene the one being read inherits, and
|
|
199
|
+
# the deepest is laid first, so this is the file that makes the node.
|
|
190
200
|
if prefix == "." and loaded == null:
|
|
191
201
|
nodes[path]["inheritedFrom"] = state.get_path()
|
|
192
202
|
# The name is the state's even for an entry laid already, since an inherited scene names its
|
|
@@ -251,8 +261,8 @@ static func _editable_in(scene_path: String, given_scene: PackedScene = null) ->
|
|
|
251
261
|
|
|
252
262
|
|
|
253
263
|
## The unique_id of each node entry in the text scene at [param scene_path], in the order its state
|
|
254
|
-
## holds them, -1 where an entry has none. All -1 for a binary scene, and for a file whose entries
|
|
255
|
-
## not number [param count], since SceneState does not give the ids to a script.
|
|
264
|
+
## holds them, -1 where an entry has none. All -1 for a binary scene, and for a file whose entries
|
|
265
|
+
## do not number [param count], since SceneState does not give the ids to a script.
|
|
256
266
|
static func _unique_ids(scene_path: String, count: int) -> Array[int]:
|
|
257
267
|
var ids: Array[int] = []
|
|
258
268
|
var _sized: int = ids.resize(count)
|
|
@@ -289,8 +299,9 @@ static func _owner_of(nodes: Dictionary, parent: String, prefix: String) -> Stri
|
|
|
289
299
|
return at if nodes.has(at) else prefix
|
|
290
300
|
|
|
291
301
|
|
|
292
|
-
## The path of the node [param owner] owns whose unique_id is [param id], or "": one under an
|
|
293
|
-
## nested below [param owner] belongs to that instance's file, whose ids are numbered
|
|
302
|
+
## The path of the node [param owner] owns whose unique_id is [param id], or "": one under an
|
|
303
|
+
## instance nested below [param owner] belongs to that instance's file, whose ids are numbered
|
|
304
|
+
## apart.
|
|
294
305
|
static func _by_id(nodes: Dictionary, owner: String, id: int) -> String:
|
|
295
306
|
for path: String in nodes:
|
|
296
307
|
if path == owner or not (owner == "." or path.begins_with(owner + "/")):
|
|
@@ -307,7 +318,7 @@ static func _by_id(nodes: Dictionary, owner: String, id: int) -> String:
|
|
|
307
318
|
static func _made(type: String) -> Dictionary:
|
|
308
319
|
var children: Array[String] = []
|
|
309
320
|
var groups: Array[String] = []
|
|
310
|
-
return {"name": "", "type": type, "properties": {}, "children": children, "groups": groups}
|
|
321
|
+
return { "name": "", "type": type, "properties": {}, "children": children, "groups": groups }
|
|
311
322
|
|
|
312
323
|
|
|
313
324
|
## Puts [param path] among [param parent]'s children, at [param at] when the file says where (a node
|
|
@@ -342,7 +353,9 @@ static func _entry(
|
|
|
342
353
|
) -> Dictionary:
|
|
343
354
|
var node: Dictionary = nodes[path]
|
|
344
355
|
var children: Array[Dictionary] = []
|
|
345
|
-
var data: Dictionary = {
|
|
356
|
+
var data: Dictionary = {
|
|
357
|
+
"name": node["name"], "type": node["type"], "path": path, "children": children,
|
|
358
|
+
}
|
|
346
359
|
# The root's file is the scene being read, so only another node's names a second scene.
|
|
347
360
|
if path != "." and node.has("instanceOf"):
|
|
348
361
|
data["instanceOf"] = node["instanceOf"]
|
|
@@ -369,7 +382,8 @@ static func _entry(
|
|
|
369
382
|
if cells >= 0:
|
|
370
383
|
data["cellCount"] = cells
|
|
371
384
|
data["cellsListedBy"] = (
|
|
372
|
-
"scene_node get with nodePath %s, which lists them as set_tilemap_cells takes them"
|
|
385
|
+
"scene_node get with nodePath %s, which lists them as set_tilemap_cells takes them"
|
|
386
|
+
% path
|
|
373
387
|
)
|
|
374
388
|
if not node.get("madeHere", false):
|
|
375
389
|
var kept: Array = node.get("setHere", [])
|
|
@@ -383,14 +397,16 @@ static func _entry(
|
|
|
383
397
|
data["childrenNotListed"] = below.size()
|
|
384
398
|
return data
|
|
385
399
|
for child: String in below:
|
|
386
|
-
children.append(
|
|
400
|
+
children.append(
|
|
401
|
+
_entry(nodes, child, include_properties, include_defaults, depth, current_depth + 1)
|
|
402
|
+
)
|
|
387
403
|
return data
|
|
388
404
|
|
|
389
405
|
|
|
390
406
|
## How many cells a TileMapLayer or TileMap of [param type] holds under the values [param given], or
|
|
391
|
-
## -1 when it is another node or the file sets none of its cells. The file keeps them as packed
|
|
392
|
-
## which a read of the tree answered with alone, so a caller decoded the base64 to learn what
|
|
393
|
-
## painted. Only cells the file sets are counted: an instance's come from its own scene.
|
|
407
|
+
## -1 when it is another node or the file sets none of its cells. The file keeps them as packed
|
|
408
|
+
## bytes, which a read of the tree answered with alone, so a caller decoded the base64 to learn what
|
|
409
|
+
## was painted. Only cells the file sets are counted: an instance's come from its own scene.
|
|
394
410
|
static func _cell_count(type: String, given: Dictionary) -> int:
|
|
395
411
|
if not ClassDB.class_exists(type) or not ClassDB.can_instantiate(type):
|
|
396
412
|
return -1
|
|
@@ -400,7 +416,10 @@ static func _cell_count(type: String, given: Dictionary) -> int:
|
|
|
400
416
|
var painted: bool = false
|
|
401
417
|
for property: Variant in given:
|
|
402
418
|
var named: String = str(property)
|
|
403
|
-
if
|
|
419
|
+
if (
|
|
420
|
+
named == "tile_map_data"
|
|
421
|
+
or (named.begins_with("layer_") and named.ends_with("/tile_data"))
|
|
422
|
+
):
|
|
404
423
|
painted = true
|
|
405
424
|
if not painted:
|
|
406
425
|
return -1
|
|
@@ -439,7 +458,10 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
439
458
|
if script != null:
|
|
440
459
|
for declared: Dictionary in script.get_script_property_list():
|
|
441
460
|
var property: String = str(declared.get("name", ""))
|
|
442
|
-
if
|
|
461
|
+
if (
|
|
462
|
+
not property.is_empty()
|
|
463
|
+
and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE
|
|
464
|
+
):
|
|
443
465
|
scripted[property] = script.get_property_default_value(property)
|
|
444
466
|
var values: Dictionary = {}
|
|
445
467
|
var defaults: Dictionary = {}
|
|
@@ -451,7 +473,10 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
451
473
|
bare.set(str(property), given[property])
|
|
452
474
|
for declared: Dictionary in bare.get_property_list():
|
|
453
475
|
var property: String = str(declared.get("name", ""))
|
|
454
|
-
if
|
|
476
|
+
if (
|
|
477
|
+
not property.is_empty()
|
|
478
|
+
and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE
|
|
479
|
+
):
|
|
455
480
|
values[property] = bare.get(property)
|
|
456
481
|
defaults[property] = plain.get(property)
|
|
457
482
|
for made: Object in [bare, plain]:
|
|
@@ -476,7 +501,7 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
476
501
|
left_out += 1
|
|
477
502
|
continue
|
|
478
503
|
answered[property] = serialiser.serialize_value(values[property])
|
|
479
|
-
return {"properties": answered, "left_out": left_out}
|
|
504
|
+
return { "properties": answered, "left_out": left_out }
|
|
480
505
|
|
|
481
506
|
|
|
482
507
|
## What a resource file holds, in the form modify takes, so a value read here can be written back.
|
|
@@ -488,10 +513,13 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
488
513
|
static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
489
514
|
var file: String = res_path.get_slice("::", 0)
|
|
490
515
|
if not FileAccess.file_exists(file):
|
|
491
|
-
return {
|
|
516
|
+
return {
|
|
517
|
+
"ok": false,
|
|
518
|
+
"error": GdhFileWalk.new().with_nearest_files("No resource at " + file, file),
|
|
519
|
+
}
|
|
492
520
|
var whole: Resource = ResourceLoader.load(file, "", ResourceLoader.CACHE_MODE_IGNORE)
|
|
493
521
|
if whole == null:
|
|
494
|
-
return {"ok": false, "error": "%s did not load as a resource." % file}
|
|
522
|
+
return { "ok": false, "error": "%s did not load as a resource." % file }
|
|
495
523
|
# The file's resource is held until the read is done, since what it keeps can depend on it: a
|
|
496
524
|
# TileSet that goes unlinks its sources, and an atlas source with no TileSet lists none of its
|
|
497
525
|
# tiles' physics, navigation or custom data.
|
|
@@ -500,14 +528,20 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
500
528
|
var id: String = res_path.trim_prefix(file + "::")
|
|
501
529
|
var inner: Resource = _kept_in(whole, file, id, {})
|
|
502
530
|
if inner == null:
|
|
503
|
-
return {"ok": false, "error": "%s keeps no resource with the id %s." % [file, id]}
|
|
531
|
+
return { "ok": false, "error": "%s keeps no resource with the id %s." % [file, id] }
|
|
504
532
|
loaded = inner
|
|
505
533
|
# A scene's own properties are its packed bundle, which says nothing a caller can use.
|
|
506
534
|
if loaded is PackedScene:
|
|
507
535
|
return {
|
|
508
536
|
"ok": false,
|
|
509
537
|
"error":
|
|
510
|
-
|
|
538
|
+
(
|
|
539
|
+
(
|
|
540
|
+
"%s is a scene: scene_tree reads its nodes, and scene_node get one node's "
|
|
541
|
+
+ "properties."
|
|
542
|
+
)
|
|
543
|
+
% res_path
|
|
544
|
+
),
|
|
511
545
|
}
|
|
512
546
|
|
|
513
547
|
var script: Script = loaded.get_script()
|
|
@@ -525,13 +559,17 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
525
559
|
untooled and file.ends_with(".tres") and not GdhShapes.STORED_APART.has(loaded.get_class())
|
|
526
560
|
)
|
|
527
561
|
var set_in_file: Dictionary = (
|
|
528
|
-
_names_set_in(
|
|
562
|
+
_names_set_in(
|
|
563
|
+
FileAccess.get_file_as_string(file), res_path.trim_prefix(file).trim_prefix("::")
|
|
564
|
+
)
|
|
529
565
|
if from_file
|
|
530
566
|
else {}
|
|
531
567
|
)
|
|
532
568
|
# A Curve's points are read off one with none, since the engine has no default for point_0.
|
|
533
569
|
var fresh: Object = (
|
|
534
|
-
ClassDB.instantiate(loaded.get_class())
|
|
570
|
+
ClassDB.instantiate(loaded.get_class())
|
|
571
|
+
if GdhShapes.STORED_APART.has(loaded.get_class())
|
|
572
|
+
else null
|
|
535
573
|
)
|
|
536
574
|
for p: Dictionary in GdhShapes.read_properties(loaded):
|
|
537
575
|
var property: String = str(p.get("name", ""))
|
|
@@ -554,7 +592,10 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
554
592
|
continue
|
|
555
593
|
properties[property] = serialiser.serialize_value(value)
|
|
556
594
|
var answer: Dictionary = {
|
|
557
|
-
"ok": true,
|
|
595
|
+
"ok": true,
|
|
596
|
+
"resourcePath": res_path,
|
|
597
|
+
"resourceType": loaded.get_class(),
|
|
598
|
+
"properties": properties,
|
|
558
599
|
}
|
|
559
600
|
if script != null:
|
|
560
601
|
answer["script"] = script.resource_path
|
|
@@ -566,8 +607,8 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
566
607
|
return answer
|
|
567
608
|
|
|
568
609
|
|
|
569
|
-
## The names the [code][resource][/code] section of [param text], a .tres file, sets, or those of
|
|
570
|
-
## embedded resource with the id [param id] when one is given, as a set.
|
|
610
|
+
## The names the [code][resource][/code] section of [param text], a .tres file, sets, or those of
|
|
611
|
+
## the embedded resource with the id [param id] when one is given, as a set.
|
|
571
612
|
static func _names_set_in(text: String, id: String) -> Dictionary:
|
|
572
613
|
var names: Dictionary = {}
|
|
573
614
|
var inside: bool = false
|
|
@@ -23,12 +23,15 @@ static func missing_reason(path: String) -> String:
|
|
|
23
23
|
if id == ResourceUID.INVALID_ID or not ResourceUID.has_id(id):
|
|
24
24
|
return (
|
|
25
25
|
path
|
|
26
|
-
+ " does not resolve: no file in the project's uid cache has it, so the file has
|
|
27
|
-
+ " project has not been imported yet (project_import refresh_uids
|
|
26
|
+
+ " does not resolve: no file in the project's uid cache has it, so the file has "
|
|
27
|
+
+ "gone or the project has not been imported yet (project_import refresh_uids "
|
|
28
|
+
+ "imports it)"
|
|
28
29
|
)
|
|
29
30
|
var resolved: String = ResourceUID.get_id_path(id)
|
|
30
31
|
return (
|
|
31
|
-
""
|
|
32
|
+
""
|
|
33
|
+
if FileAccess.file_exists(resolved)
|
|
34
|
+
else path + " names " + resolved + ", which does not exist"
|
|
32
35
|
)
|
|
33
36
|
return "" if FileAccess.file_exists(path) else path + " does not exist"
|
|
34
37
|
|
|
@@ -41,7 +44,9 @@ func with_nearest_files(said: String, path: String) -> String:
|
|
|
41
44
|
# One the refusal names is the file it refuses, as a script given for a scene is. Word by word,
|
|
42
45
|
# since res://brain.gd is inside res://brain.gdd.
|
|
43
46
|
var named: Array[String] = []
|
|
44
|
-
for word: String in said.replace("\n", " ").replace(",", " ").replace(";", " ").split(
|
|
47
|
+
for word: String in said.replace("\n", " ").replace(",", " ").replace(";", " ").split(
|
|
48
|
+
" ", false
|
|
49
|
+
):
|
|
45
50
|
named.append(word.rstrip(".:"))
|
|
46
51
|
var namesakes: Array[String] = []
|
|
47
52
|
for file: String in _namesakes(path):
|
|
@@ -78,7 +83,9 @@ func with_nearest_files(said: String, path: String) -> String:
|
|
|
78
83
|
)
|
|
79
84
|
if files.size() <= GdhRead.NAMED_CHILDREN:
|
|
80
85
|
return "%s. The project's .%s files are %s." % [said, extension, ", ".join(files)]
|
|
81
|
-
return
|
|
86
|
+
return (
|
|
87
|
+
"%s. None of the project's %d .%s files is named like it." % [said, files.size(), extension]
|
|
88
|
+
)
|
|
82
89
|
|
|
83
90
|
|
|
84
91
|
# The files in [param path]'s folder named as it is with another extension, leaving out the import
|
|
@@ -13,55 +13,6 @@ var _dependencies: GdhDependencies = null
|
|
|
13
13
|
var _reading: String = ""
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
## What one class body declares, the script's own or an inner class's.
|
|
17
|
-
class GdhMembers:
|
|
18
|
-
var declared_class_name: Variant = null
|
|
19
|
-
var extends_name: String = "RefCounted"
|
|
20
|
-
var signals: Array[Dictionary] = []
|
|
21
|
-
var variables: Array[Dictionary] = []
|
|
22
|
-
var functions: Array[Dictionary] = []
|
|
23
|
-
var constants: Array[Dictionary] = []
|
|
24
|
-
var enums: Array[Dictionary] = []
|
|
25
|
-
var inner_classes: Array[Dictionary] = []
|
|
26
|
-
## The `##` comment the class is documented with, empty when it has none.
|
|
27
|
-
var doc: String = ""
|
|
28
|
-
## The index of the last line the body holds.
|
|
29
|
-
var last: int = -1
|
|
30
|
-
## An inner class's name and the line its `class` is on.
|
|
31
|
-
var inner_name: String = ""
|
|
32
|
-
var line: int = 0
|
|
33
|
-
|
|
34
|
-
## The inner class as its entry under inner_classes, in the fields a script's own are in.
|
|
35
|
-
func as_inner() -> Dictionary:
|
|
36
|
-
return {
|
|
37
|
-
"name": inner_name,
|
|
38
|
-
"line": line,
|
|
39
|
-
"extends": extends_name,
|
|
40
|
-
"signals": signals,
|
|
41
|
-
"variables": variables,
|
|
42
|
-
"functions": functions,
|
|
43
|
-
"constants": constants,
|
|
44
|
-
"enums": enums,
|
|
45
|
-
"inner_classes": inner_classes,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
## Whether a member is declared yet, past which a `##` block no longer documents the class.
|
|
49
|
-
func declares_members() -> bool:
|
|
50
|
-
return not (
|
|
51
|
-
signals.is_empty()
|
|
52
|
-
and variables.is_empty()
|
|
53
|
-
and functions.is_empty()
|
|
54
|
-
and constants.is_empty()
|
|
55
|
-
and enums.is_empty()
|
|
56
|
-
and inner_classes.is_empty()
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
## [param lines] as the class's documentation, when it has none yet.
|
|
60
|
-
func take_doc(lines: Array[String]) -> void:
|
|
61
|
-
if doc.is_empty() and not lines.is_empty():
|
|
62
|
-
doc = "\n".join(PackedStringArray(lines))
|
|
63
|
-
|
|
64
|
-
|
|
65
16
|
func _init(p_log: GdhLog) -> void:
|
|
66
17
|
_log = p_log
|
|
67
18
|
|
|
@@ -116,7 +67,7 @@ func get_gdscript_info(params: Dictionary) -> Dictionary:
|
|
|
116
67
|
var declaring: String = str(declared_by[used])
|
|
117
68
|
if declaring == full_script_path:
|
|
118
69
|
continue
|
|
119
|
-
classes_used.append({"name": used, "path": declaring})
|
|
70
|
+
classes_used.append({ "name": used, "path": declaring })
|
|
120
71
|
if not declaring in dependencies:
|
|
121
72
|
dependencies.append(declaring)
|
|
122
73
|
|
|
@@ -133,7 +84,7 @@ func get_gdscript_info(params: Dictionary) -> Dictionary:
|
|
|
133
84
|
var autoloads_used: Array[Dictionary] = []
|
|
134
85
|
for used: String in GdhDependencies.classes_used_in(content, global_autoloads):
|
|
135
86
|
if str(global_autoloads[used]) != full_script_path:
|
|
136
|
-
autoloads_used.append({"name": used, "path": str(global_autoloads[used])})
|
|
87
|
+
autoloads_used.append({ "name": used, "path": str(global_autoloads[used]) })
|
|
137
88
|
|
|
138
89
|
var answer: Dictionary = {
|
|
139
90
|
"path": script_path,
|
|
@@ -149,7 +100,8 @@ func get_gdscript_info(params: Dictionary) -> Dictionary:
|
|
|
149
100
|
"dependencies": dependencies,
|
|
150
101
|
"classes_used": classes_used,
|
|
151
102
|
"autoloads_used": autoloads_used,
|
|
152
|
-
"line_count":
|
|
103
|
+
"line_count":
|
|
104
|
+
lines.size() - 1 if content.is_empty() or content.ends_with("\n") else lines.size(),
|
|
153
105
|
}
|
|
154
106
|
if not own.doc.is_empty():
|
|
155
107
|
answer["class_doc"] = own.doc
|
|
@@ -335,7 +287,9 @@ func _inner_class(
|
|
|
335
287
|
body_margin = indent
|
|
336
288
|
break
|
|
337
289
|
var body: GdhMembers = (
|
|
338
|
-
GdhMembers.new()
|
|
290
|
+
GdhMembers.new()
|
|
291
|
+
if body_margin.is_empty()
|
|
292
|
+
else _members(lines, at + 1, body_margin, dependencies)
|
|
339
293
|
)
|
|
340
294
|
if body_margin.is_empty():
|
|
341
295
|
body.last = at
|
|
@@ -352,7 +306,10 @@ static func _not_a_script(path: String) -> String:
|
|
|
352
306
|
var said: String = "%s is not a GDScript file, and script_info reads .gd files only" % path
|
|
353
307
|
match path.get_extension().to_lower():
|
|
354
308
|
"tscn", "scn":
|
|
355
|
-
return
|
|
309
|
+
return (
|
|
310
|
+
said
|
|
311
|
+
+ ": scene_tree reads a scene's nodes, and with properties the script each carries."
|
|
312
|
+
)
|
|
356
313
|
"tres", "res":
|
|
357
314
|
return said + ": resource_edit read reads a resource."
|
|
358
315
|
return said + "."
|
|
@@ -397,7 +354,7 @@ func _native_ancestor(base: String, from_path: String) -> String:
|
|
|
397
354
|
if path in seen:
|
|
398
355
|
return ""
|
|
399
356
|
seen.append(path)
|
|
400
|
-
var above: Dictionary = get_gdscript_info({"script_path": path})
|
|
357
|
+
var above: Dictionary = get_gdscript_info({ "script_path": path })
|
|
401
358
|
if above.is_empty():
|
|
402
359
|
return ""
|
|
403
360
|
current = str(above.get("extends", ""))
|
|
@@ -422,7 +379,7 @@ func _add_inherited(answer: Dictionary, seen: Array[String]) -> void:
|
|
|
422
379
|
if base_path.is_empty() or base_path in seen:
|
|
423
380
|
break
|
|
424
381
|
seen.append(base_path)
|
|
425
|
-
var above: Dictionary = get_gdscript_info({"script_path": base_path})
|
|
382
|
+
var above: Dictionary = get_gdscript_info({ "script_path": base_path })
|
|
426
383
|
# A read that failed answers with an empty dictionary rather than with a flag, so that is
|
|
427
384
|
# what is asked. `_script_named` has already checked the file is there, which leaves only
|
|
428
385
|
# an open that fails, and the chain stops without recording a file nothing was read from.
|
|
@@ -479,12 +436,14 @@ func _parse_signal(line: String, line_num: int) -> Dictionary:
|
|
|
479
436
|
# comma answered with two.
|
|
480
437
|
var paren_end: int = _closing_paren(signal_text, paren_start)
|
|
481
438
|
if paren_end > paren_start:
|
|
482
|
-
for p: String in _arguments_in(
|
|
439
|
+
for p: String in _arguments_in(
|
|
440
|
+
signal_text.substr(paren_start + 1, paren_end - paren_start - 1)
|
|
441
|
+
):
|
|
483
442
|
params.append(_parse_param(p))
|
|
484
443
|
else:
|
|
485
444
|
signal_name = signal_text
|
|
486
445
|
|
|
487
|
-
return {"name": signal_name, "params": params, "line": line_num}
|
|
446
|
+
return { "name": signal_name, "params": params, "line": line_num }
|
|
488
447
|
|
|
489
448
|
|
|
490
449
|
func _parse_constant(line: String, line_num: int) -> Dictionary:
|
|
@@ -507,7 +466,7 @@ func _parse_constant(line: String, line_num: int) -> Dictionary:
|
|
|
507
466
|
else:
|
|
508
467
|
name = const_text
|
|
509
468
|
|
|
510
|
-
var constant: Dictionary = {"name": name, "value": value, "type": type_hint, "line": line_num}
|
|
469
|
+
var constant: Dictionary = { "name": name, "value": value, "type": type_hint, "line": line_num }
|
|
511
470
|
# A constant has its value's type whether or not it is written with :=.
|
|
512
471
|
if type_hint.is_empty():
|
|
513
472
|
_infer(constant, value)
|
|
@@ -588,7 +547,7 @@ func _parse_enum(line: String, line_num: int) -> Dictionary:
|
|
|
588
547
|
else:
|
|
589
548
|
enum_name = enum_text
|
|
590
549
|
|
|
591
|
-
return {"name": enum_name, "values": values, "line": line_num}
|
|
550
|
+
return { "name": enum_name, "values": values, "line": line_num }
|
|
592
551
|
|
|
593
552
|
|
|
594
553
|
func _parse_variable(line: String, line_num: int) -> Dictionary:
|
|
@@ -609,7 +568,7 @@ func _parse_variable(line: String, line_num: int) -> Dictionary:
|
|
|
609
568
|
|
|
610
569
|
var var_pos: int = line.find("var ")
|
|
611
570
|
if var_pos == -1:
|
|
612
|
-
return {"name": "", "line": line_num}
|
|
571
|
+
return { "name": "", "line": line_num }
|
|
613
572
|
|
|
614
573
|
var var_text: String = line.substr(var_pos + 4).strip_edges()
|
|
615
574
|
# A colon closing the line opens the variable's setter and getter, and was read as the end of
|
|
@@ -652,10 +611,12 @@ func _parse_variable(line: String, line_num: int) -> Dictionary:
|
|
|
652
611
|
"is_onready": is_onready,
|
|
653
612
|
# One per class rather than per instance, which a caller reading a counter needs to know.
|
|
654
613
|
"is_static": GdhPatterns.without_annotations(line).begins_with("static "),
|
|
655
|
-
"line": line_num
|
|
614
|
+
"line": line_num,
|
|
656
615
|
}
|
|
657
616
|
# Only := types a variable by its value; one written with = alone holds any type.
|
|
658
|
-
var walrus: bool =
|
|
617
|
+
var walrus: bool = (
|
|
618
|
+
"=" in var_text and var_text.split("=", true, 1)[0].strip_edges().ends_with(":")
|
|
619
|
+
)
|
|
659
620
|
if walrus and type_hint.is_empty():
|
|
660
621
|
_infer(variable, default_value)
|
|
661
622
|
return variable
|
|
@@ -701,7 +662,7 @@ func _parse_function(line: String, line_num: int, annotated: String = "") -> Dic
|
|
|
701
662
|
# Said rather than left to be inferred: an abstract method has no body, so a caller that
|
|
702
663
|
# saw only the declaration would otherwise take it for one whose body it failed to read.
|
|
703
664
|
"is_abstract": "abstract" in GdhPatterns.annotations_on(annotated),
|
|
704
|
-
"line": line_num
|
|
665
|
+
"line": line_num,
|
|
705
666
|
}
|
|
706
667
|
|
|
707
668
|
|
|
@@ -849,7 +810,7 @@ func _parse_param(param_text: String) -> Dictionary:
|
|
|
849
810
|
var stated_default: Variant = null
|
|
850
811
|
if "=" in param_text:
|
|
851
812
|
stated_default = default_value
|
|
852
|
-
return {"name": name, "type": type_hint, "default": stated_default, "is_rest": is_rest}
|
|
813
|
+
return { "name": name, "type": type_hint, "default": stated_default, "is_rest": is_rest }
|
|
853
814
|
|
|
854
815
|
|
|
855
816
|
func _collect_dependencies(line: String, into: Array[String]) -> void:
|
|
@@ -877,3 +838,63 @@ func _extract_dependencies(line: String) -> Array[String]:
|
|
|
877
838
|
deps.append(m.get_string(1))
|
|
878
839
|
|
|
879
840
|
return deps
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
## What one class body declares, the script's own or an inner class's.
|
|
844
|
+
class GdhMembers:
|
|
845
|
+
var declared_class_name: Variant = null
|
|
846
|
+
|
|
847
|
+
var extends_name: String = "RefCounted"
|
|
848
|
+
|
|
849
|
+
var signals: Array[Dictionary] = []
|
|
850
|
+
|
|
851
|
+
var variables: Array[Dictionary] = []
|
|
852
|
+
|
|
853
|
+
var functions: Array[Dictionary] = []
|
|
854
|
+
|
|
855
|
+
var constants: Array[Dictionary] = []
|
|
856
|
+
|
|
857
|
+
var enums: Array[Dictionary] = []
|
|
858
|
+
|
|
859
|
+
var inner_classes: Array[Dictionary] = []
|
|
860
|
+
|
|
861
|
+
## The `##` comment the class is documented with, empty when it has none.
|
|
862
|
+
var doc: String = ""
|
|
863
|
+
|
|
864
|
+
## The index of the last line the body holds.
|
|
865
|
+
var last: int = -1
|
|
866
|
+
|
|
867
|
+
## An inner class's name and the line its `class` is on.
|
|
868
|
+
var inner_name: String = ""
|
|
869
|
+
|
|
870
|
+
var line: int = 0
|
|
871
|
+
|
|
872
|
+
## The inner class as its entry under inner_classes, in the fields a script's own are in.
|
|
873
|
+
func as_inner() -> Dictionary:
|
|
874
|
+
return {
|
|
875
|
+
"name": inner_name,
|
|
876
|
+
"line": line,
|
|
877
|
+
"extends": extends_name,
|
|
878
|
+
"signals": signals,
|
|
879
|
+
"variables": variables,
|
|
880
|
+
"functions": functions,
|
|
881
|
+
"constants": constants,
|
|
882
|
+
"enums": enums,
|
|
883
|
+
"inner_classes": inner_classes,
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
## Whether a member is declared yet, past which a `##` block no longer documents the class.
|
|
887
|
+
func declares_members() -> bool:
|
|
888
|
+
return not (
|
|
889
|
+
signals.is_empty()
|
|
890
|
+
and variables.is_empty()
|
|
891
|
+
and functions.is_empty()
|
|
892
|
+
and constants.is_empty()
|
|
893
|
+
and enums.is_empty()
|
|
894
|
+
and inner_classes.is_empty()
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
## [param lines] as the class's documentation, when it has none yet.
|
|
898
|
+
func take_doc(lines: Array[String]) -> void:
|
|
899
|
+
if doc.is_empty() and not lines.is_empty():
|
|
900
|
+
doc = "\n".join(PackedStringArray(lines))
|