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.
- package/build/cli.js +131 -47
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
- 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 +114 -39
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
- 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 +37 -12
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
- 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 +41 -28
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
- 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 +181 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
- 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 +66 -0
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
- 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 +37 -12
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
- 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 +38 -20
- 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 +211 -0
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
- 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 +84 -35
- 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 +139 -93
- package/build/godot/operations/editor_tools.gd +9 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +114 -39
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +105 -66
- package/build/godot/operations/gdscript_authoring.gd +138 -39
- 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 +63 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +144 -27
- 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 +37 -12
- 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/package.json +1 -1
|
@@ -5,20 +5,19 @@ const GdhRead = preload("reading.gd")
|
|
|
5
5
|
const GdhFileWalk = preload("file_walk.gd")
|
|
6
6
|
const GdhLog = preload("logger.gd")
|
|
7
7
|
const GdhResourceFiles = preload("resource_files.gd")
|
|
8
|
-
|
|
9
8
|
## What a reverse search reads unless told otherwise: the resource and code files, and the settings
|
|
10
|
-
## files that name resources, "godot" for project.godot, "cfg" for plugin.cfg and
|
|
11
|
-
## and "import" for the sidecars that name import scripts and external
|
|
9
|
+
## files that name resources, "godot" for project.godot, "cfg" for plugin.cfg and
|
|
10
|
+
## export_presets.cfg, and "import" for the sidecars that name import scripts and external
|
|
11
|
+
## materials.
|
|
12
12
|
const PROJECT_FILE: String = "res://project.godot"
|
|
13
13
|
const DEFAULT_USAGE_KINDS: Array[String] = [
|
|
14
|
-
"tscn", "tres", "gd", "gdshader", "gdshaderinc", "godot", "cfg", "import"
|
|
14
|
+
"tscn", "tres", "gd", "gdshader", "gdshaderinc", "godot", "cfg", "import",
|
|
15
15
|
]
|
|
16
|
-
## The files written in the shader language, whose comments are // and /* */ rather than #, which in
|
|
17
|
-
## shader starts a preprocessor directive.
|
|
16
|
+
## The files written in the shader language, whose comments are // and /* */ rather than #, which in
|
|
17
|
+
## a shader starts a preprocessor directive.
|
|
18
18
|
const SHADER_KINDS: Array[String] = ["gdshader", "gdshaderinc"]
|
|
19
19
|
## One string literal of either quote, its contents captured by the one it opened with.
|
|
20
20
|
const STRING_LITERAL: String = "\"((?:[^\"\\\\]|\\\\.)*)\"|'((?:[^'\\\\]|\\\\.)*)'"
|
|
21
|
-
|
|
22
21
|
# What a dependency reference looks like in source, and how the path is read out of each form.
|
|
23
22
|
const REFERENCE_PATTERNS: Array[String] = [
|
|
24
23
|
"res://[^\"'\\s\\]\\)]+",
|
|
@@ -39,28 +38,6 @@ var _classes_declared: Dictionary = {}
|
|
|
39
38
|
var _classes_read: bool = false
|
|
40
39
|
|
|
41
40
|
|
|
42
|
-
# Everything one dependency walk carries: the settings it was started with, and the state it
|
|
43
|
-
# accumulates as it recurses. Kept together so the recursion passes one object rather than
|
|
44
|
-
# six positional arguments that have to stay in the same order at every call site.
|
|
45
|
-
class GdhDependencyWalk:
|
|
46
|
-
var max_depth: int
|
|
47
|
-
var include_built_in: bool
|
|
48
|
-
var visited: Dictionary = {}
|
|
49
|
-
var path_stack: Array[String] = []
|
|
50
|
-
var circular_references: Array
|
|
51
|
-
## Whether a file the depth stops at is read for how many files it names, which the walk doing
|
|
52
|
-
## that reading does not do again for those.
|
|
53
|
-
var counts_beyond: bool
|
|
54
|
-
|
|
55
|
-
func _init(
|
|
56
|
-
p_max_depth: int, p_include_built_in: bool, p_circular_references: Array, p_counts_beyond: bool = true
|
|
57
|
-
) -> void:
|
|
58
|
-
max_depth = p_max_depth
|
|
59
|
-
include_built_in = p_include_built_in
|
|
60
|
-
circular_references = p_circular_references
|
|
61
|
-
counts_beyond = p_counts_beyond
|
|
62
|
-
|
|
63
|
-
|
|
64
41
|
func _init(p_log: GdhLog) -> void:
|
|
65
42
|
_log = p_log
|
|
66
43
|
|
|
@@ -88,7 +65,9 @@ func get_dependencies(params: Dictionary) -> Dictionary:
|
|
|
88
65
|
return _log.failure(
|
|
89
66
|
_files.with_nearest_files("Resource file does not exist: " + full_path, full_path)
|
|
90
67
|
)
|
|
91
|
-
var walk: GdhDependencyWalk = GdhDependencyWalk.new(
|
|
68
|
+
var walk: GdhDependencyWalk = GdhDependencyWalk.new(
|
|
69
|
+
max_depth, include_built_in, circular_references
|
|
70
|
+
)
|
|
92
71
|
dependencies[full_path] = _analyze_resource(full_path, 0, walk)
|
|
93
72
|
|
|
94
73
|
var references: int = 0
|
|
@@ -105,7 +84,7 @@ func get_dependencies(params: Dictionary) -> Dictionary:
|
|
|
105
84
|
# `exists: false` among the entries, which a caller reading the summary never met.
|
|
106
85
|
var missing_files: Array[Dictionary] = []
|
|
107
86
|
for path: String in missing:
|
|
108
|
-
missing_files.append({"path": path, "referenced_by": missing[path]})
|
|
87
|
+
missing_files.append({ "path": path, "referenced_by": missing[path] })
|
|
109
88
|
|
|
110
89
|
return {
|
|
111
90
|
"dependencies": dependencies,
|
|
@@ -119,8 +98,8 @@ func get_dependencies(params: Dictionary) -> Dictionary:
|
|
|
119
98
|
"total_dependencies": distinct.size(),
|
|
120
99
|
"total_references": references,
|
|
121
100
|
"circular_count": circular_references.size(),
|
|
122
|
-
"missing": missing_files
|
|
123
|
-
}
|
|
101
|
+
"missing": missing_files,
|
|
102
|
+
},
|
|
124
103
|
}
|
|
125
104
|
|
|
126
105
|
|
|
@@ -137,10 +116,14 @@ func missing_references() -> Array[Dictionary]:
|
|
|
137
116
|
var naming: Array = missing.get_or_add(path, [])
|
|
138
117
|
if not file in naming:
|
|
139
118
|
naming.append(file)
|
|
140
|
-
for file: String in _files.find_files_with_extensions(
|
|
119
|
+
for file: String in _files.find_files_with_extensions(
|
|
120
|
+
"res://", GdhResourceFiles.TEXT_RESOURCE_KINDS
|
|
121
|
+
):
|
|
141
122
|
var content: String = FileAccess.get_file_as_string(file)
|
|
142
123
|
var moved: Dictionary = _moved_by_uid(content)
|
|
143
|
-
for line: RegExMatch in GdhPatterns.compiled("\\[ext_resource[^\\]]*\\]").search_all(
|
|
124
|
+
for line: RegExMatch in GdhPatterns.compiled("\\[ext_resource[^\\]]*\\]").search_all(
|
|
125
|
+
content
|
|
126
|
+
):
|
|
144
127
|
var at: RegExMatch = GdhPatterns.compiled('path="([^"]+)"').search(line.get_string())
|
|
145
128
|
if at == null or moved.has(at.get_string(1)):
|
|
146
129
|
continue
|
|
@@ -162,7 +145,7 @@ func missing_references() -> Array[Dictionary]:
|
|
|
162
145
|
for path: String in paths:
|
|
163
146
|
var naming: Array = missing[path]
|
|
164
147
|
naming.sort()
|
|
165
|
-
found.append({"path": path, "referenced_by": naming})
|
|
148
|
+
found.append({ "path": path, "referenced_by": naming })
|
|
166
149
|
return found
|
|
167
150
|
|
|
168
151
|
|
|
@@ -183,12 +166,16 @@ func _loaded_and_missing(script: String) -> Array[String]:
|
|
|
183
166
|
continue
|
|
184
167
|
var calls: Dictionary = {}
|
|
185
168
|
for loading: RegExMatch in (
|
|
186
|
-
GdhPatterns
|
|
169
|
+
GdhPatterns
|
|
170
|
+
. compiled("(?:(?<![\\w.])|ResourceLoader\\.)load\\(\\s*(?=[\"'])")
|
|
171
|
+
. search_all(code)
|
|
187
172
|
):
|
|
188
173
|
calls[loading.get_end()] = true
|
|
189
174
|
for literal: RegExMatch in GdhPatterns.compiled(STRING_LITERAL).search_all(code):
|
|
190
175
|
var single: String = literal.get_string(2)
|
|
191
|
-
var path: String = _resolved(
|
|
176
|
+
var path: String = _resolved(
|
|
177
|
+
literal.get_string(1) if single.is_empty() else single, script
|
|
178
|
+
)
|
|
192
179
|
if calls.has(literal.get_start()):
|
|
193
180
|
loaded.append(path)
|
|
194
181
|
else:
|
|
@@ -200,7 +187,11 @@ func _loaded_and_missing(script: String) -> Array[String]:
|
|
|
200
187
|
if not named.has(path) and not missing.has(path):
|
|
201
188
|
missing.append(path)
|
|
202
189
|
continue
|
|
203
|
-
if
|
|
190
|
+
if (
|
|
191
|
+
not path.begins_with("res://")
|
|
192
|
+
or path.begins_with("res://.")
|
|
193
|
+
or path.get_extension().is_empty()
|
|
194
|
+
):
|
|
204
195
|
continue
|
|
205
196
|
if not named.has(path) and not FileAccess.file_exists(path) and not missing.has(path):
|
|
206
197
|
missing.append(path)
|
|
@@ -239,7 +230,11 @@ static func missing_line(entry: Dictionary) -> String:
|
|
|
239
230
|
var naming: PackedStringArray = PackedStringArray(referring)
|
|
240
231
|
return (
|
|
241
232
|
"%s does not exist, and %s %s to it"
|
|
242
|
-
% [
|
|
233
|
+
% [
|
|
234
|
+
str(entry.get("path", "")),
|
|
235
|
+
", ".join(naming),
|
|
236
|
+
"refers" if naming.size() == 1 else "refer",
|
|
237
|
+
]
|
|
243
238
|
)
|
|
244
239
|
|
|
245
240
|
|
|
@@ -276,7 +271,7 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
276
271
|
by_autoload = GdhPatterns.compiled("\\b" + _regex_escaped(autoload_named) + "\\b")
|
|
277
272
|
var target: Dictionary = {
|
|
278
273
|
"path": resource_path,
|
|
279
|
-
"uid": str(GdhResourceFiles.uid_of(resource_path).get("uid", "")) if exists else ""
|
|
274
|
+
"uid": str(GdhResourceFiles.uid_of(resource_path).get("uid", "")) if exists else "",
|
|
280
275
|
}
|
|
281
276
|
|
|
282
277
|
var all_files: Array[String] = _usage_files(file_types)
|
|
@@ -296,9 +291,9 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
296
291
|
var text: String = file.get_as_text()
|
|
297
292
|
file.close()
|
|
298
293
|
var lines: PackedStringArray = text.split("\n")
|
|
299
|
-
# A class is used by name only in a script's code. In a scene or a resource the same word is
|
|
300
|
-
# node's name or a caption, and each was counted as a use of the class; a shader has no
|
|
301
|
-
# for one at all.
|
|
294
|
+
# A class is used by name only in a script's code. In a scene or a resource the same word is
|
|
295
|
+
# a node's name or a caption, and each was counted as a use of the class; a shader has no
|
|
296
|
+
# use for one at all.
|
|
302
297
|
var code: bool = file_path.get_extension() == "gd"
|
|
303
298
|
# In a shader # opens a directive, so an #include was answered as a comment, and its
|
|
304
299
|
# comments are // and /* */.
|
|
@@ -326,9 +321,13 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
326
321
|
var trimmed: String = line.strip_edges()
|
|
327
322
|
var kind: String = ""
|
|
328
323
|
var extra: Dictionary = {}
|
|
329
|
-
var loaded: String =
|
|
324
|
+
var loaded: String = (
|
|
325
|
+
_loaded_by_uid(line) if trimmed.begins_with("[ext_resource") else ""
|
|
326
|
+
)
|
|
330
327
|
var written: RegExMatch = GdhPatterns.compiled('path="([^"]+)"').search(line)
|
|
331
|
-
var written_path: String =
|
|
328
|
+
var written_path: String = (
|
|
329
|
+
"" if written == null else _resolved(written.get_string(1), file_path)
|
|
330
|
+
)
|
|
332
331
|
# A mention in a comment is not a use, and both were answered as one. The question a
|
|
333
332
|
# reverse walk is asked is whether anything still uses a class, and a project that
|
|
334
333
|
# documents itself names its collaborators in `##` links: four references to one class,
|
|
@@ -339,10 +338,14 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
339
338
|
kind = "doc"
|
|
340
339
|
elif (code and trimmed.begins_with("#")) or name_in_comment:
|
|
341
340
|
kind = "comment"
|
|
342
|
-
elif
|
|
341
|
+
elif (
|
|
342
|
+
not shader_lines.is_empty()
|
|
343
|
+
and not _names_target(shader_lines[i], file_path, target)
|
|
344
|
+
):
|
|
343
345
|
kind = "comment"
|
|
344
346
|
elif by_its_path and not loaded.is_empty() and loaded != target["path"]:
|
|
345
|
-
# The engine loads the file the uid names, so the path written here is used by
|
|
347
|
+
# The engine loads the file the uid names, so the path written here is used by
|
|
348
|
+
# nothing.
|
|
346
349
|
kind = "stale_path"
|
|
347
350
|
extra["loads"] = loaded
|
|
348
351
|
elif by_its_path:
|
|
@@ -362,7 +365,7 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
362
365
|
if GdhPatterns.without_annotations(line).begins_with("extends ")
|
|
363
366
|
else "class_name"
|
|
364
367
|
)
|
|
365
|
-
var found: Dictionary = {"line": i + 1, "kind": kind, "text": trimmed}
|
|
368
|
+
var found: Dictionary = { "line": i + 1, "kind": kind, "text": trimmed }
|
|
366
369
|
found.merge(extra)
|
|
367
370
|
references.append(found)
|
|
368
371
|
by_kind[kind] = GdhRead.as_int(by_kind.get(kind, 0)) + 1
|
|
@@ -370,13 +373,16 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
370
373
|
in_code += 1
|
|
371
374
|
|
|
372
375
|
if not references.is_empty():
|
|
373
|
-
usages.append({"file": file_path, "references": references})
|
|
376
|
+
usages.append({ "file": file_path, "references": references })
|
|
374
377
|
total += references.size()
|
|
375
378
|
|
|
376
379
|
if not exists and usages.is_empty():
|
|
377
380
|
return _log.failure(
|
|
378
381
|
_files.with_nearest_files(
|
|
379
|
-
|
|
382
|
+
(
|
|
383
|
+
"Resource file does not exist: %s, and nothing in the project refers to it"
|
|
384
|
+
% resource_path
|
|
385
|
+
),
|
|
380
386
|
resource_path
|
|
381
387
|
)
|
|
382
388
|
)
|
|
@@ -413,7 +419,9 @@ func _declared_class_name(path: String) -> String:
|
|
|
413
419
|
return ""
|
|
414
420
|
while not file.eof_reached():
|
|
415
421
|
# Annotations first, since `@abstract class_name X` is one line and this read it as none.
|
|
416
|
-
var found: RegExMatch = GdhPatterns.declared_class(
|
|
422
|
+
var found: RegExMatch = GdhPatterns.declared_class(
|
|
423
|
+
GdhPatterns.without_annotations(file.get_line())
|
|
424
|
+
)
|
|
417
425
|
if found != null:
|
|
418
426
|
file.close()
|
|
419
427
|
return found.get_string(1)
|
|
@@ -443,11 +451,11 @@ func _usage_files(kinds: Array) -> Array[String]:
|
|
|
443
451
|
return found
|
|
444
452
|
|
|
445
453
|
|
|
446
|
-
# Whether [param line], in [param file_path], names the target by path or by UID. Every string on
|
|
447
|
-
# line is read as a path the way the engine reads it: a res:// path as it is, a uid:// by what
|
|
448
|
-
# names, and anything else relative to the file it is in, which is how `preload("sibling.gd")`
|
|
449
|
-
# plugin.cfg's `script="plugin.gd"` name their files. Only the quoted form of the project path
|
|
450
|
-
# looked for, so neither of those was found, and nor was a scene named by its UID.
|
|
454
|
+
# Whether [param line], in [param file_path], names the target by path or by UID. Every string on
|
|
455
|
+
# the line is read as a path the way the engine reads it: a res:// path as it is, a uid:// by what
|
|
456
|
+
# it names, and anything else relative to the file it is in, which is how `preload("sibling.gd")`
|
|
457
|
+
# and a plugin.cfg's `script="plugin.gd"` name their files. Only the quoted form of the project path
|
|
458
|
+
# was looked for, so neither of those was found, and nor was a scene named by its UID.
|
|
451
459
|
func _names_target(line: String, file_path: String, target: Dictionary) -> bool:
|
|
452
460
|
var path: String = target["path"]
|
|
453
461
|
var uid: String = target["uid"]
|
|
@@ -566,15 +574,17 @@ func _count_recursive(deps: Array[Dictionary]) -> int:
|
|
|
566
574
|
return count
|
|
567
575
|
|
|
568
576
|
|
|
569
|
-
func _analyze_resource(
|
|
577
|
+
func _analyze_resource(
|
|
578
|
+
path: String, current_depth: int, walk: GdhDependencyWalk
|
|
579
|
+
) -> Array[Dictionary]:
|
|
570
580
|
var deps: Array[Dictionary] = []
|
|
571
581
|
|
|
572
582
|
if current_depth >= walk.max_depth:
|
|
573
583
|
return deps
|
|
574
584
|
|
|
575
|
-
# Reused only when it was walked with as much depth left as now. A resource first reached deep
|
|
576
|
-
# the walk has a list cut short at the limit, and handing that to a nearer reach answered
|
|
577
|
-
# chain that stopped where nothing stopped it.
|
|
585
|
+
# Reused only when it was walked with as much depth left as now. A resource first reached deep
|
|
586
|
+
# in the walk has a list cut short at the limit, and handing that to a nearer reach answered
|
|
587
|
+
# with a chain that stopped where nothing stopped it.
|
|
578
588
|
var remaining: int = walk.max_depth - current_depth
|
|
579
589
|
if walk.visited.has(path):
|
|
580
590
|
var cached: Dictionary = walk.visited[path]
|
|
@@ -589,8 +599,9 @@ func _analyze_resource(path: String, current_depth: int, walk: GdhDependencyWalk
|
|
|
589
599
|
var content: String = file.get_as_text()
|
|
590
600
|
file.close()
|
|
591
601
|
# A script's comments name paths as examples, and read as references they were answered as
|
|
592
|
-
# dependencies, one of a file that does not exist: the shipped serialisation module's
|
|
593
|
-
# about `["res://addons/x/plugin.cfg"]` was a dependency of every project with the
|
|
602
|
+
# dependencies, one of a file that does not exist: the shipped serialisation module's
|
|
603
|
+
# comment about `["res://addons/x/plugin.cfg"]` was a dependency of every project with the
|
|
604
|
+
# addon.
|
|
594
605
|
if path.get_extension() == "gd":
|
|
595
606
|
content = _code_lines(content)
|
|
596
607
|
elif path.get_extension() in SHADER_KINDS:
|
|
@@ -604,8 +615,8 @@ func _analyze_resource(path: String, current_depth: int, walk: GdhDependencyWalk
|
|
|
604
615
|
var written: String = _resolved(_referenced_path(m.get_string()), path)
|
|
605
616
|
var dep_path: String = str(moved.get(written, written))
|
|
606
617
|
|
|
607
|
-
# A uid the project does not know names no file, and loading it fails: passed over
|
|
608
|
-
# no res:// path, a script's load of one went unsaid while the game printed
|
|
618
|
+
# A uid the project does not know names no file, and loading it fails: passed over
|
|
619
|
+
# as no res:// path, a script's load of one went unsaid while the game printed
|
|
609
620
|
# "Unrecognized UID" at that line.
|
|
610
621
|
if dep_path.begins_with("uid://"):
|
|
611
622
|
_depend(deps, path, dep_path, {}, current_depth, walk)
|
|
@@ -613,7 +624,8 @@ func _analyze_resource(path: String, current_depth: int, walk: GdhDependencyWalk
|
|
|
613
624
|
if not dep_path.begins_with("res://"):
|
|
614
625
|
continue
|
|
615
626
|
# A literal naming a directory, or the start of a path a script builds as it runs
|
|
616
|
-
# ("res://levels/" + name), names no file, and walked as one it was a missing
|
|
627
|
+
# ("res://levels/" + name), names no file, and walked as one it was a missing
|
|
628
|
+
# dependency.
|
|
617
629
|
if dep_path.get_extension().is_empty():
|
|
618
630
|
continue
|
|
619
631
|
|
|
@@ -623,32 +635,34 @@ func _analyze_resource(path: String, current_depth: int, walk: GdhDependencyWalk
|
|
|
623
635
|
if not walk.include_built_in and dep_path.begins_with("res://."):
|
|
624
636
|
continue
|
|
625
637
|
|
|
626
|
-
var named_by: Dictionary = {"stale_path": written} if dep_path != written else {}
|
|
638
|
+
var named_by: Dictionary = { "stale_path": written } if dep_path != written else {}
|
|
627
639
|
if unknown.has(dep_path):
|
|
628
640
|
named_by["unknown_uid"] = unknown[dep_path]
|
|
629
641
|
_depend(deps, path, dep_path, named_by, current_depth, walk)
|
|
630
642
|
|
|
631
|
-
# A script uses another by its class_name as surely as by a preload, and only the reverse
|
|
632
|
-
# looked for that: asked what a script depends on, the answer left out the script it
|
|
633
|
-
# variable with, while asking what used that script named this one. An autoload's
|
|
634
|
-
# name is the same kind of use, of the script registered under it.
|
|
643
|
+
# A script uses another by its class_name as surely as by a preload, and only the reverse
|
|
644
|
+
# walk looked for that: asked what a script depends on, the answer left out the script it
|
|
645
|
+
# typed a variable with, while asking what used that script named this one. An autoload's
|
|
646
|
+
# global name is the same kind of use, of the script registered under it.
|
|
635
647
|
if path.get_extension() == "gd":
|
|
636
648
|
var classes: Dictionary = classes_declared()
|
|
637
649
|
for used: String in classes_used_in(content, classes):
|
|
638
|
-
_depend(deps, path, str(classes[used]), {"by_class": used}, current_depth, walk)
|
|
650
|
+
_depend(deps, path, str(classes[used]), { "by_class": used }, current_depth, walk)
|
|
639
651
|
var autoloads: Dictionary = autoloads_declared()
|
|
640
652
|
for used: String in classes_used_in(content, autoloads):
|
|
641
|
-
_depend(
|
|
653
|
+
_depend(
|
|
654
|
+
deps, path, str(autoloads[used]), { "by_autoload": used }, current_depth, walk
|
|
655
|
+
)
|
|
642
656
|
|
|
643
657
|
walk.path_stack.pop_back()
|
|
644
|
-
walk.visited[path] = {"deps": deps, "remaining": remaining}
|
|
658
|
+
walk.visited[path] = { "deps": deps, "remaining": remaining }
|
|
645
659
|
return deps
|
|
646
660
|
|
|
647
661
|
|
|
648
|
-
# Adds [param dep_path] to [param deps], what [param path] depends on, walked as far as the depth
|
|
649
|
-
# allows, once however many times it is named. [param named_by] is the class or autoload name
|
|
650
|
-
# named by, as by_class or by_autoload, and the path written when the engine loads another,
|
|
651
|
-
# stale_path; empty for a path that is the file.
|
|
662
|
+
# Adds [param dep_path] to [param deps], what [param path] depends on, walked as far as the depth
|
|
663
|
+
# left allows, once however many times it is named. [param named_by] is the class or autoload name
|
|
664
|
+
# it was named by, as by_class or by_autoload, and the path written when the engine loads another,
|
|
665
|
+
# as stale_path; empty for a path that is the file.
|
|
652
666
|
func _depend(
|
|
653
667
|
deps: Array[Dictionary],
|
|
654
668
|
path: String,
|
|
@@ -662,14 +676,15 @@ func _depend(
|
|
|
662
676
|
for existing: Dictionary in deps:
|
|
663
677
|
if existing.get("path", "") == dep_path:
|
|
664
678
|
return
|
|
665
|
-
# A uid still written as one is one the project does not know, and asked about, the engine
|
|
666
|
-
# an error for it.
|
|
679
|
+
# A uid still written as one is one the project does not know, and asked about, the engine
|
|
680
|
+
# prints an error for it.
|
|
667
681
|
var dep_info: Dictionary = {
|
|
668
|
-
"path": dep_path,
|
|
682
|
+
"path": dep_path,
|
|
683
|
+
"exists": not dep_path.begins_with("uid://") and FileAccess.file_exists(dep_path),
|
|
669
684
|
}
|
|
670
685
|
dep_info.merge(named_by)
|
|
671
|
-
# Marked where the reference closing the cycle is. Marked inside the file reached again, the
|
|
672
|
-
# answered that file as a dependency of itself.
|
|
686
|
+
# Marked where the reference closing the cycle is. Marked inside the file reached again, the
|
|
687
|
+
# tree answered that file as a dependency of itself.
|
|
673
688
|
if dep_path in walk.path_stack:
|
|
674
689
|
var cycle: Array[String] = walk.path_stack.slice(walk.path_stack.find(dep_path))
|
|
675
690
|
cycle.append(dep_path)
|
|
@@ -683,8 +698,8 @@ func _depend(
|
|
|
683
698
|
if sub_deps.size() > 0:
|
|
684
699
|
dep_info["dependencies"] = sub_deps
|
|
685
700
|
elif dep_info["exists"] and walk.counts_beyond:
|
|
686
|
-
# A file the depth stopped at read the same as one naming nothing, and a missing file past
|
|
687
|
-
# went unsaid.
|
|
701
|
+
# A file the depth stopped at read the same as one naming nothing, and a missing file past
|
|
702
|
+
# it went unsaid.
|
|
688
703
|
var beyond: GdhDependencyWalk = GdhDependencyWalk.new(1, walk.include_built_in, [], false)
|
|
689
704
|
var named: int = _analyze_resource(dep_path, 0, beyond).size()
|
|
690
705
|
if named > 0:
|
|
@@ -728,12 +743,12 @@ func list_script_classes(_params: Dictionary) -> Dictionary:
|
|
|
728
743
|
names.sort()
|
|
729
744
|
var classes: Array[Dictionary] = []
|
|
730
745
|
for name: Variant in names:
|
|
731
|
-
classes.append({"name": str(name), "path": str(declared[name])})
|
|
732
|
-
return {"classes": classes, "count": classes.size()}
|
|
746
|
+
classes.append({ "name": str(name), "path": str(declared[name]) })
|
|
747
|
+
return { "classes": classes, "count": classes.size() }
|
|
733
748
|
|
|
734
749
|
|
|
735
|
-
## The names in [param classes] that the code in [param content] uses, in the order first used:
|
|
736
|
-
## outside strings and comments, so a class named in prose or in a message is not a use of it.
|
|
750
|
+
## The names in [param classes] that the code in [param content] uses, in the order first used:
|
|
751
|
+
## words outside strings and comments, so a class named in prose or in a message is not a use of it.
|
|
737
752
|
static func classes_used_in(content: String, classes: Dictionary) -> Array[String]:
|
|
738
753
|
var used: Array[String] = []
|
|
739
754
|
if classes.is_empty():
|
|
@@ -764,8 +779,9 @@ static func _resolved(written: String, from: String) -> String:
|
|
|
764
779
|
|
|
765
780
|
# The ext_resource paths in [param content] whose uid leads elsewhere, each with the file the uid
|
|
766
781
|
# names. The engine loads an ext_resource by its uid whenever the uid is known, whatever the path
|
|
767
|
-
# says, so a file moved outside the editor still loads, and the walk called it missing; every
|
|
768
|
-
# reads the old path, the bare res:// one included, so the paths are mapped before any of
|
|
782
|
+
# says, so a file moved outside the editor still loads, and the walk called it missing; every
|
|
783
|
+
# pattern reads the old path, the bare res:// one included, so the paths are mapped before any of
|
|
784
|
+
# them runs.
|
|
769
785
|
static func _moved_by_uid(content: String) -> Dictionary:
|
|
770
786
|
var moved: Dictionary = {}
|
|
771
787
|
for line: RegExMatch in GdhPatterns.compiled("\\[ext_resource[^\\]]*\\]").search_all(content):
|
|
@@ -838,8 +854,8 @@ static func _shader_code(content: String) -> String:
|
|
|
838
854
|
return "".join(PackedStringArray(kept))
|
|
839
855
|
|
|
840
856
|
|
|
841
|
-
# The path inside a preload(), load(), #include or ext_resource match; a bare res:// match is the
|
|
842
|
-
# An ext_resource is read first, since its type or its path can hold the word load.
|
|
857
|
+
# The path inside a preload(), load(), #include or ext_resource match; a bare res:// match is the
|
|
858
|
+
# path. An ext_resource is read first, since its type or its path can hold the word load.
|
|
843
859
|
func _referenced_path(matched: String) -> String:
|
|
844
860
|
var expression: String = ""
|
|
845
861
|
if matched.begins_with("ext_resource"):
|
|
@@ -851,3 +867,33 @@ func _referenced_path(matched: String) -> String:
|
|
|
851
867
|
var inner: RegEx = GdhPatterns.compiled(expression)
|
|
852
868
|
var inner_match: RegExMatch = inner.search(matched)
|
|
853
869
|
return inner_match.get_string(1) if inner_match else matched
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
# Everything one dependency walk carries: the settings it was started with, and the state it
|
|
873
|
+
# accumulates as it recurses. Kept together so the recursion passes one object rather than
|
|
874
|
+
# six positional arguments that have to stay in the same order at every call site.
|
|
875
|
+
class GdhDependencyWalk:
|
|
876
|
+
var max_depth: int
|
|
877
|
+
|
|
878
|
+
var include_built_in: bool
|
|
879
|
+
|
|
880
|
+
var visited: Dictionary = {}
|
|
881
|
+
|
|
882
|
+
var path_stack: Array[String] = []
|
|
883
|
+
|
|
884
|
+
var circular_references: Array
|
|
885
|
+
|
|
886
|
+
## Whether a file the depth stops at is read for how many files it names, which the walk doing
|
|
887
|
+
## that reading does not do again for those.
|
|
888
|
+
var counts_beyond: bool
|
|
889
|
+
|
|
890
|
+
func _init(
|
|
891
|
+
p_max_depth: int,
|
|
892
|
+
p_include_built_in: bool,
|
|
893
|
+
p_circular_references: Array,
|
|
894
|
+
p_counts_beyond: bool = true
|
|
895
|
+
) -> void:
|
|
896
|
+
max_depth = p_max_depth
|
|
897
|
+
include_built_in = p_include_built_in
|
|
898
|
+
circular_references = p_circular_references
|
|
899
|
+
counts_beyond = p_counts_beyond
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## The editor addon's scene and resource tools, run by a headless engine for a project no editor
|
|
4
3
|
## serves.
|
|
5
4
|
##
|
|
@@ -16,7 +15,6 @@ const GdhSignalTools = preload("../addons/gdharness_editor/tools/signal_tools.gd
|
|
|
16
15
|
const GdhTilesetTools = preload("../addons/gdharness_editor/tools/tileset_tools.gd")
|
|
17
16
|
const GdhSceneFile = preload("../addons/gdharness_editor/scene_file.gd")
|
|
18
17
|
const GdhSceneUids = preload("../addons/gdharness_editor/scene_uids.gd")
|
|
19
|
-
|
|
20
18
|
const SCENE_COMMANDS: Array[String] = [
|
|
21
19
|
"create_scene",
|
|
22
20
|
"save_scene",
|
|
@@ -58,7 +56,7 @@ static func run(params: Dictionary) -> Dictionary:
|
|
|
58
56
|
var command: String = str(params.get("command", ""))
|
|
59
57
|
var given: Variant = params.get("arguments", {})
|
|
60
58
|
if not given is Dictionary:
|
|
61
|
-
return {"ok": false, "error": "arguments must be an object."}
|
|
59
|
+
return { "ok": false, "error": "arguments must be an object." }
|
|
62
60
|
var arguments: Dictionary = given
|
|
63
61
|
var tools: Node = null
|
|
64
62
|
if command in SCENE_COMMANDS:
|
|
@@ -76,7 +74,7 @@ static func run(params: Dictionary) -> Dictionary:
|
|
|
76
74
|
elif command in ANIMATION_STATE_COMMANDS:
|
|
77
75
|
tools = GdhAnimationStates.new()
|
|
78
76
|
else:
|
|
79
|
-
return {"ok": false, "error": "No editor tool runs headless as " + command}
|
|
77
|
+
return { "ok": false, "error": "No editor tool runs headless as " + command }
|
|
80
78
|
var answer: Variant = tools.call(command, arguments)
|
|
81
79
|
tools.free()
|
|
82
80
|
if answer is Dictionary:
|
|
@@ -86,11 +84,11 @@ static func run(params: Dictionary) -> Dictionary:
|
|
|
86
84
|
if not GdhSceneUids.by_path.is_empty():
|
|
87
85
|
answered["namedByPath"] = (
|
|
88
86
|
(
|
|
89
|
-
"What this saved names %s by path rather than by uid, which loads the same. A
|
|
90
|
-
+ "file's own header is resolved through the project's uid cache,
|
|
91
|
-
+ "%s yet, as with a file made since the project was last
|
|
92
|
-
+ "now would warn on every load. project_import
|
|
93
|
-
+ "writes after it name %s by uid."
|
|
87
|
+
"What this saved names %s by path rather than by uid, which loads the same. A "
|
|
88
|
+
+ "uid in a file's own header is resolved through the project's uid cache, "
|
|
89
|
+
+ "which holds none for %s yet, as with a file made since the project was last "
|
|
90
|
+
+ "scanned, so a uid written now would warn on every load. project_import "
|
|
91
|
+
+ "refresh_uids scans the project, and writes after it name %s by uid."
|
|
94
92
|
)
|
|
95
93
|
% [
|
|
96
94
|
", ".join(GdhSceneUids.by_path),
|
|
@@ -100,5 +98,6 @@ static func run(params: Dictionary) -> Dictionary:
|
|
|
100
98
|
)
|
|
101
99
|
return answered
|
|
102
100
|
return {
|
|
103
|
-
"ok": false,
|
|
101
|
+
"ok": false,
|
|
102
|
+
"error": "%s answered %s rather than an object." % [command, type_string(typeof(answer))],
|
|
104
103
|
}
|