gdharness 1.0.23 → 1.0.24

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.
@@ -6,6 +6,51 @@ extends RefCounted
6
6
 
7
7
  const Values = preload("runtime_values.gd")
8
8
 
9
+ ## The calls a path makes on a list or a map: those that take no arguments and change nothing.
10
+ ##
11
+ ## Named rather than asked of the engine, because asking about a method a list does not have logs an
12
+ ## error into the game's output, and a typo in a path is not the game's error. The ones that change
13
+ ## what they are called on, such as clear(), sort() and pop_back(), are left out: a path is how a
14
+ ## read and a wait reach what they read, and a wait asks many times.
15
+ const LIST_CALLS: Array[String] = [
16
+ "size",
17
+ "is_empty",
18
+ "front",
19
+ "back",
20
+ "max",
21
+ "min",
22
+ "pick_random",
23
+ "hash",
24
+ "duplicate",
25
+ "duplicate_deep",
26
+ "is_read_only",
27
+ "is_typed",
28
+ "get_typed_builtin",
29
+ "get_typed_class_name",
30
+ "get_typed_script",
31
+ ]
32
+ const MAP_CALLS: Array[String] = [
33
+ "size",
34
+ "is_empty",
35
+ "keys",
36
+ "values",
37
+ "hash",
38
+ "duplicate",
39
+ "duplicate_deep",
40
+ "is_read_only",
41
+ "is_typed",
42
+ "is_typed_key",
43
+ "is_typed_value",
44
+ "get_typed_key_builtin",
45
+ "get_typed_key_class_name",
46
+ "get_typed_key_script",
47
+ "get_typed_value_builtin",
48
+ "get_typed_value_class_name",
49
+ "get_typed_value_script",
50
+ ]
51
+ ## The calls an empty list has nothing to answer, which the engine reports as an error when asked.
52
+ const NEEDS_AN_ELEMENT: Array[String] = ["front", "back", "pick_random"]
53
+
9
54
 
10
55
  ## What [param reaching] names something on, which is the node itself until the name has a colon
11
56
  ## in it, and the last name along that path.
@@ -23,7 +68,9 @@ const Values = preload("runtime_values.gd")
23
68
  ## and walks into what it returned. Some of what hangs off a node is only reachable by asking:
24
69
  ## which control holds the focus is a question for the viewport a control is in, and the viewport
25
70
  ## is a method's answer rather than a property. Spelled with the brackets so a reader sees a call
26
- ## where one happens, and so a method can never shadow a property of the same name.
71
+ ## where one happens, and so a method can never shadow a property of the same name. A list or a map
72
+ ## answers the calls that read it, "board:size()" or "inbox:keys()", listed in [constant LIST_CALLS]
73
+ ## and [constant MAP_CALLS].
27
74
  ##
28
75
  ## Answers with a `message` instead when a step along the way is not there or holds something that
29
76
  ## is not an object, naming the step rather than the whole path: "/root/Main:_game has no property
@@ -117,9 +164,14 @@ static func nothing_under(holder: Variant, named: String, called: String) -> Str
117
164
  static func can_read(holder: Variant, named: String) -> bool:
118
165
  if holder is Array:
119
166
  var items: Array = holder
167
+ var list_call: String = method_of(named)
168
+ if not list_call.is_empty():
169
+ return list_call in LIST_CALLS and not (items.is_empty() and list_call in NEEDS_AN_ELEMENT)
120
170
  return _index_in(named, items.size()) != -1
121
171
  if holder is Dictionary:
122
172
  var map: Dictionary = holder
173
+ if not method_of(named).is_empty():
174
+ return method_of(named) in MAP_CALLS
123
175
  return map.has(named) or map.has(StringName(named))
124
176
  if holder is Object:
125
177
  var object: Object = holder
@@ -173,6 +225,8 @@ static func write(holder: Variant, named: String, value: Variant) -> void:
173
225
  ## What [param holder] holds under [param named], or answers to it when [param named] is a call.
174
226
  ## Only ever called once [method can_read] agrees.
175
227
  static func read_under(holder: Variant, named: String) -> Variant:
228
+ if (holder is Array or holder is Dictionary) and not method_of(named).is_empty():
229
+ return Callable.create(holder, method_of(named)).call()
176
230
  if holder is Array:
177
231
  var items: Array = holder
178
232
  return items[_index_in(named, items.size())]
@@ -207,10 +261,17 @@ static func _index_in(named: String, size: int) -> int:
207
261
  ## so a list says how long it is and a map says what it is keyed by. The step is named rather than
208
262
  ## the whole path, because the step is the typo.
209
263
  static func nothing_there(holder: Variant, named: String, called_as: String) -> String:
264
+ var container_call: String = method_of(named)
210
265
  if holder is Array:
211
266
  var items: Array = holder
267
+ if container_call in NEEDS_AN_ELEMENT and items.is_empty():
268
+ return "%s is an empty list, so it has no %s" % [called_as, named]
269
+ if not container_call.is_empty():
270
+ return container_calls_refused(called_as, "a list", container_call)
212
271
  return "%s is a list of %d, so there is no %s in it" % [called_as, items.size(), named]
213
272
  if holder is Dictionary:
273
+ if not container_call.is_empty():
274
+ return container_calls_refused(called_as, "a map", container_call)
214
275
  var map: Dictionary = holder
215
276
  var keys: Array = map.keys()
216
277
  # Eight of them, because a map keyed by something unexpected is told by the first few and a
@@ -242,6 +303,19 @@ static func nothing_there(holder: Variant, named: String, called_as: String) ->
242
303
  return "%s has no property %s" % [called_as, named]
243
304
 
244
305
 
306
+ ## Why [param method] is not a call a path makes on [param sort] ("a list" or "a map"), naming
307
+ ## the calls it does make.
308
+ static func container_calls_refused(called_as: String, sort: String, method: String) -> String:
309
+ var calls: Array[String] = LIST_CALLS if sort == "a list" else MAP_CALLS
310
+ var spelled: Array[String] = []
311
+ for each: String in calls:
312
+ spelled.append(each + "()")
313
+ return (
314
+ "%s is %s, and %s() is not one of the calls a path makes on one, which read it and take no arguments: %s"
315
+ % [called_as, sort, method, ", ".join(spelled)]
316
+ )
317
+
318
+
245
319
  ## Whether [param holder] declares [param named]. Asked of the list rather than read, because a
246
320
  ## property read back as null answers it wrongly.
247
321
  static func _has_property(holder: Object, named: String) -> bool:
@@ -695,17 +695,33 @@ func call_method(params: Dictionary) -> Dictionary:
695
695
  if reached.has("message"):
696
696
  return reached
697
697
 
698
- # A list or a map can be stepped through on the way, and is not a thing with methods when the
699
- # path stops on one: said in those terms rather than as "has no method", which reads as a
700
- # misspelling of a method that was never going to be there.
698
+ # A list or a map answers the calls a path makes on one, and nothing else: said in those terms
699
+ # rather than as "has no method", which reads as a misspelling of a method that was never going
700
+ # to be there, and with the element spelled out, since a method on what it holds is the usual aim.
701
701
  if not reached["holder"] is Object:
702
702
  var sort: String = "a list" if reached["holder"] is Array else "a map"
703
+ var spelled: String = str(reached["name"])
704
+ if Paths.method_of(spelled).is_empty():
705
+ spelled += "()"
706
+ if args.is_empty() and Paths.can_read(reached["holder"], spelled):
707
+ var answered: Variant = Paths.read_under(reached["holder"], spelled)
708
+ return {
709
+ "type": "method_result",
710
+ "path": node_path,
711
+ "method": method,
712
+ "result": _values.serialize(answered),
713
+ }
714
+ var why: String = (
715
+ "%s:%s takes no arguments" % [reached["called"], spelled]
716
+ if Paths.can_read(reached["holder"], spelled)
717
+ else Paths.nothing_there(reached["holder"], spelled, str(reached["called"]))
718
+ )
703
719
  return {
704
720
  "type": "error",
705
721
  "message":
706
722
  (
707
- "%s is %s, which has no methods: name the element to call it on, as in %s:0:%s"
708
- % [reached["called"], sort, reached["called"], reached["name"]]
723
+ "%s. A method of what %s holds is called on the element, as in %s:0:%s"
724
+ % [why, sort, reached["called"], reached["name"]]
709
725
  )
710
726
  }
711
727
  var holder: Object = reached["holder"]