From 28127ce2248ddbfd8bc9c44e38fa2d5e1f655fe1 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Tue, 28 Jan 2020 01:31:49 +0100 Subject: [PATCH] Print suggestions when requesting a nonexistent InputMap action Co-authored-by: Marc Gilleron --- core/input_map.cpp | 50 ++++++++++++++++++++++++++++++++++++---------- core/input_map.h | 1 + 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/core/input_map.cpp b/core/input_map.cpp index d855ff2f209f..fb5644ceb7a9 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -55,9 +55,38 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals); } +/** + * Returns an nonexistent action error message with a suggestion of the closest + * matching action name (if possible). + */ +String InputMap::_suggest_actions(const StringName &p_action) const { + + List actions = get_actions(); + StringName closest_action; + float closest_similarity = 0.0; + + // Find the most action with the most similar name. + for (List::Element *E = actions.front(); E; E = E->next()) { + const float similarity = String(E->get()).similarity(p_action); + + if (similarity > closest_similarity) { + closest_action = E->get(); + closest_similarity = similarity; + } + } + + String error_message = vformat("The InputMap action \"%s\" doesn't exist.", p_action); + + if (closest_similarity >= 0.4) { + // Only include a suggestion in the error message if it's similar enough. + error_message += vformat(" Did you mean \"%s\"?", closest_action); + } + return error_message; +} + void InputMap::add_action(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\"."); input_map[p_action] = Action(); static int last_id = 1; input_map[p_action].id = last_id; @@ -67,7 +96,8 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) { void InputMap::erase_action(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + input_map.erase(p_action); } @@ -128,7 +158,7 @@ bool InputMap::has_action(const StringName &p_action) const { void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); input_map[p_action].deadzone = p_deadzone; } @@ -136,7 +166,8 @@ void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) void InputMap::action_add_event(const StringName &p_action, const Ref &p_event) { ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + if (_find_event(input_map[p_action], p_event)) return; //already gots @@ -145,13 +176,14 @@ void InputMap::action_add_event(const StringName &p_action, const Ref &p_event) { - ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action)); + return (_find_event(input_map[p_action], p_event) != NULL); } void InputMap::action_erase_event(const StringName &p_action, const Ref &p_event) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); List >::Element *E = _find_event(input_map[p_action], p_event); if (E) { @@ -164,7 +196,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref &p_event, const StringName bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const { Map::Element *E = input_map.find(p_action); - ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action)); Ref input_event_action = p_event; if (input_event_action.is_valid()) { @@ -333,8 +365,6 @@ void InputMap::load_default() { key.instance(); key->set_scancode(KEY_END); action_add_event("ui_end", key); - - //set("display/window/handheld/orientation", "landscape"); } InputMap::InputMap() { diff --git a/core/input_map.h b/core/input_map.h index 57bebadd2ded..14063726103f 100644 --- a/core/input_map.h +++ b/core/input_map.h @@ -59,6 +59,7 @@ class InputMap : public Object { Array _get_action_list(const StringName &p_action); Array _get_actions(); + String _suggest_actions(const StringName &p_action) const; protected: static void _bind_methods();