From 1afd045c297b44fc24db0dd121407db0509d1f21 Mon Sep 17 00:00:00 2001 From: Ishigh1 Date: Sat, 1 Jun 2024 14:32:41 +0200 Subject: [PATCH] Core: Fix auto-fill in the text client when clicking on a hint suggestion (#3267) --- MultiServer.py | 24 +------------ Utils.py | 35 +++++++++++++++++++ kvui.py | 16 +++------ .../general/test_client_server_interaction.py | 23 ++++++++++++ 4 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 test/general/test_client_server_interaction.py diff --git a/MultiServer.py b/MultiServer.py index 2c08b0b4eb08..22375da2b3c5 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -37,7 +37,7 @@ import NetUtils import Utils -from Utils import version_tuple, restricted_loads, Version, async_start +from Utils import version_tuple, restricted_loads, Version, async_start, get_intended_text from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, NetworkPlayer, Permission, NetworkSlot, \ SlotType, LocationStore @@ -1095,28 +1095,6 @@ def json_format_send_event(net_item: NetworkItem, receiving_player: int): "item": net_item} -def get_intended_text(input_text: str, possible_answers) -> typing.Tuple[str, bool, str]: - picks = Utils.get_fuzzy_results(input_text, possible_answers, limit=2) - if len(picks) > 1: - dif = picks[0][1] - picks[1][1] - if picks[0][1] == 100: - return picks[0][0], True, "Perfect Match" - elif picks[0][1] < 75: - return picks[0][0], False, f"Didn't find something that closely matches '{input_text}', " \ - f"did you mean '{picks[0][0]}'? ({picks[0][1]}% sure)" - elif dif > 5: - return picks[0][0], True, "Close Match" - else: - return picks[0][0], False, f"Too many close matches for '{input_text}', " \ - f"did you mean '{picks[0][0]}'? ({picks[0][1]}% sure)" - else: - if picks[0][1] > 90: - return picks[0][0], True, "Only Option Match" - else: - return picks[0][0], False, f"Didn't find something that closely matches '{input_text}', " \ - f"did you mean '{picks[0][0]}'? ({picks[0][1]}% sure)" - - class CommandMeta(type): def __new__(cls, name, bases, attrs): commands = attrs["commands"] = {} diff --git a/Utils.py b/Utils.py index 9f6837215c5e..eea81a2d3201 100644 --- a/Utils.py +++ b/Utils.py @@ -622,6 +622,41 @@ def get_fuzzy_ratio(word1: str, word2: str) -> float: ) +def get_intended_text(input_text: str, possible_answers) -> typing.Tuple[str, bool, str]: + picks = get_fuzzy_results(input_text, possible_answers, limit=2) + if len(picks) > 1: + dif = picks[0][1] - picks[1][1] + if picks[0][1] == 100: + return picks[0][0], True, "Perfect Match" + elif picks[0][1] < 75: + return picks[0][0], False, f"Didn't find something that closely matches '{input_text}', " \ + f"did you mean '{picks[0][0]}'? ({picks[0][1]}% sure)" + elif dif > 5: + return picks[0][0], True, "Close Match" + else: + return picks[0][0], False, f"Too many close matches for '{input_text}', " \ + f"did you mean '{picks[0][0]}'? ({picks[0][1]}% sure)" + else: + if picks[0][1] > 90: + return picks[0][0], True, "Only Option Match" + else: + return picks[0][0], False, f"Didn't find something that closely matches '{input_text}', " \ + f"did you mean '{picks[0][0]}'? ({picks[0][1]}% sure)" + + +def get_input_text_from_response(text: str, command: str) -> typing.Optional[str]: + if "did you mean " in text: + for question in ("Didn't find something that closely matches", + "Too many close matches"): + if text.startswith(question): + name = get_text_between(text, "did you mean '", + "'? (") + return f"!{command} {name}" + elif text.startswith("Missing: "): + return text.replace("Missing: ", "!hint_location ") + return None + + def open_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typing.Iterable[str]]], suggest: str = "") \ -> typing.Optional[str]: logging.info(f"Opening file input dialog for {title}.") diff --git a/kvui.py b/kvui.py index 98aa9516b266..e9e495aef366 100644 --- a/kvui.py +++ b/kvui.py @@ -64,7 +64,7 @@ fade_in_animation = Animation(opacity=0, duration=0) + Animation(opacity=1, duration=0.25) from NetUtils import JSONtoTextParser, JSONMessagePart, SlotType -from Utils import async_start +from Utils import async_start, get_input_text_from_response if typing.TYPE_CHECKING: import CommonClient @@ -285,16 +285,10 @@ def on_touch_down(self, touch): temp = MarkupLabel(text=self.text).markup text = "".join(part for part in temp if not part.startswith(("[color", "[/color]", "[ref=", "[/ref]"))) cmdinput = App.get_running_app().textinput - if not cmdinput.text and " did you mean " in text: - for question in ("Didn't find something that closely matches, did you mean ", - "Too many close matches, did you mean "): - if text.startswith(question): - name = Utils.get_text_between(text, question, - "? (") - cmdinput.text = f"!{App.get_running_app().last_autofillable_command} {name}" - break - elif not cmdinput.text and text.startswith("Missing: "): - cmdinput.text = text.replace("Missing: ", "!hint_location ") + if not cmdinput.text: + input_text = get_input_text_from_response(text, App.get_running_app().last_autofillable_command) + if input_text is not None: + cmdinput.text = input_text Clipboard.copy(text.replace("&", "&").replace("&bl;", "[").replace("&br;", "]")) return self.parent.select_with_touch(self.index, touch) diff --git a/test/general/test_client_server_interaction.py b/test/general/test_client_server_interaction.py new file mode 100644 index 000000000000..17de91517409 --- /dev/null +++ b/test/general/test_client_server_interaction.py @@ -0,0 +1,23 @@ +import unittest + +from Utils import get_intended_text, get_input_text_from_response + + +class TestClient(unittest.TestCase): + def test_autofill_hint_from_fuzzy_hint(self) -> None: + tests = ( + ("item", ["item1", "item2"]), # Multiple close matches + ("itm", ["item1", "item21"]), # No close match, multiple option + ("item", ["item1"]), # No close match, single option + ("item", ["\"item\" 'item' (item)"]), # Testing different special characters + ) + + for input_text, possible_answers in tests: + item_name, usable, response = get_intended_text(input_text, possible_answers) + self.assertFalse(usable, "This test must be updated, it seems get_fuzzy_results behavior changed") + + hint_command = get_input_text_from_response(response, "hint") + self.assertIsNotNone(hint_command, + "The response to fuzzy hints is no longer recognized by the hint autofill") + self.assertEqual(hint_command, f"!hint {item_name}", + "The hint command autofilled by the response is not correct")