90 lines
3.2 KiB
GDScript
90 lines
3.2 KiB
GDScript
@tool
|
|
extends RefCounted
|
|
|
|
## Read-only access to version-correct Godot class metadata.
|
|
|
|
const ErrorCodes := preload("res://addons/godot_ai/utils/error_codes.gd")
|
|
const ClassIntrospection := preload("res://addons/godot_ai/utils/class_introspection.gd")
|
|
const FuzzySuggestions := preload("res://addons/godot_ai/utils/fuzzy_suggestions.gd")
|
|
|
|
func get_class_info(params: Dictionary) -> Dictionary:
|
|
var requested_class: String = params.get("class_name", "")
|
|
if requested_class.is_empty():
|
|
return ErrorCodes.make(
|
|
ErrorCodes.MISSING_REQUIRED_PARAM,
|
|
"Missing required param: class_name"
|
|
)
|
|
if not ClassDB.class_exists(requested_class):
|
|
var script_class := _global_script_class(requested_class)
|
|
if not script_class.is_empty():
|
|
return _script_class_error(requested_class, script_class)
|
|
return _unknown_class_error(requested_class)
|
|
if params.has("limit") and int(params.get("limit")) < 0:
|
|
return ErrorCodes.make(
|
|
ErrorCodes.INVALID_PARAMS,
|
|
"limit must be >= 0; use limit=0 only when an unlimited section is needed"
|
|
)
|
|
var section_check := ClassIntrospection.validate_sections(
|
|
params.get("sections", ClassIntrospection.DEFAULT_SECTIONS)
|
|
)
|
|
if not section_check.invalid.is_empty():
|
|
return _invalid_sections_error(section_check.invalid)
|
|
return {"data": ClassIntrospection.build(requested_class, params)}
|
|
|
|
|
|
static func _unknown_class_error(requested_class: String) -> Dictionary:
|
|
var suggestions := _suggest_classes(requested_class)
|
|
var message := "Unknown Godot class: %s" % requested_class
|
|
if not suggestions.is_empty():
|
|
message += ". Did you mean: %s?" % ", ".join(suggestions)
|
|
var result := ErrorCodes.make(ErrorCodes.VALUE_OUT_OF_RANGE, message)
|
|
result["error"]["data"] = {"suggestions": suggestions}
|
|
return result
|
|
|
|
|
|
static func _suggest_classes(requested_class: String) -> Array[String]:
|
|
return FuzzySuggestions.rank(requested_class, ClassDB.get_class_list())
|
|
|
|
|
|
static func _global_script_class(requested_class: String) -> Dictionary:
|
|
for raw_info in ProjectSettings.get_global_class_list():
|
|
var info: Dictionary = raw_info
|
|
if info.get("class", "") == requested_class:
|
|
return info
|
|
return {}
|
|
|
|
|
|
static func _script_class_error(requested_class: String, script_class: Dictionary) -> Dictionary:
|
|
var path := str(script_class.get("path", ""))
|
|
var base := str(script_class.get("base", ""))
|
|
var message := (
|
|
"%s is a project script class, not a ClassDB class. "
|
|
+ "Use script_manage(op=\"find_symbols\", params={\"path\": \"%s\"}) for script symbols."
|
|
) % [requested_class, path]
|
|
var result := ErrorCodes.make(ErrorCodes.WRONG_TYPE, message)
|
|
result["error"]["data"] = {
|
|
"script_class": true,
|
|
"class_name": requested_class,
|
|
"base_class": base,
|
|
"path": path,
|
|
}
|
|
return result
|
|
|
|
|
|
static func _invalid_sections_error(invalid_sections: Array[String]) -> Dictionary:
|
|
var suggestions := {}
|
|
for section in invalid_sections:
|
|
suggestions[section] = FuzzySuggestions.rank(
|
|
section,
|
|
ClassIntrospection.KNOWN_SECTIONS,
|
|
3,
|
|
0.3
|
|
)
|
|
var message := "Unknown class-info section(s): %s. Valid sections: %s" % [
|
|
", ".join(invalid_sections),
|
|
", ".join(ClassIntrospection.KNOWN_SECTIONS),
|
|
]
|
|
var result := ErrorCodes.make(ErrorCodes.INVALID_PARAMS, message)
|
|
result["error"]["data"] = {"suggestions": suggestions}
|
|
return result
|