diff --git a/inputremapper/injection/macros/parse.py b/inputremapper/injection/macros/parse.py index 68ac5a74..c9be0132 100644 --- a/inputremapper/injection/macros/parse.py +++ b/inputremapper/injection/macros/parse.py @@ -315,7 +315,7 @@ def _parse_recurse(code, context, macro_instance=None, depth=0): def handle_plus_syntax(macro): - """transform a + b + c to m(a, m(b, m(c, h())))""" + """transform a + b + c to modify(a,modify(b,modify(c,hold())))""" if "+" not in macro: return macro @@ -333,9 +333,9 @@ def handle_plus_syntax(macro): raise ValueError(f'Invalid syntax for "{macro}"') depth += 1 - output += f"m({chunk}," + output += f"modify({chunk}," - output += "h()" + output += "hold()" output += depth * ")" logger.debug('Transformed "%s" to "%s"', macro, output) @@ -396,14 +396,16 @@ def parse(macro, context=None, return_errors=False): Parameters ---------- macro : string - "r(3, k(a).w(10))" - "r(2, k(a).k(KEY_A)).k(b)" - "w(1000).m(Shift_L, r(2, k(a))).w(10, 20).k(b)" + "repeat(3, key(a).wait(10))" + "repeat(2, key(a).key(KEY_A)).key(b)" + "wait(1000).modify(Shift_L, repeat(2, k(a))).wait(10, 20).key(b)" context : Context, or None for use in Frontend return_errors : bool If True, returns errors as a string or None if parsing worked. If False, returns the parsed macro. """ + macro = clean(macro) + try: macro = handle_plus_syntax(macro) except Exception as error: @@ -412,8 +414,6 @@ def parse(macro, context=None, return_errors=False): logger.debug("".join(traceback.format_tb(error.__traceback__)).strip()) return f"{error.__class__.__name__}: {str(error)}" if return_errors else None - macro = clean(macro) - if return_errors: logger.debug("checking the syntax of %s", macro) else: diff --git a/tests/unit/test_macros.py b/tests/unit/test_macros.py index 32b741cb..99815e9a 100644 --- a/tests/unit/test_macros.py +++ b/tests/unit/test_macros.py @@ -65,7 +65,7 @@ from inputremapper.configs.preset import Preset from inputremapper.configs.system_mapping import system_mapping from inputremapper.utils import PRESS, RELEASE -from tests.test import quick_cleanup, new_event +from tests.test import logger, quick_cleanup, new_event class MacroTestBase(unittest.IsolatedAsyncioTestCase): @@ -234,7 +234,7 @@ class TestMacros(MacroTestBase): self.assertTupleEqual(_split_keyword_arg("a=r(2, KEY_A)"), ("a", "r(2, KEY_A)")) self.assertTupleEqual(_split_keyword_arg('a="=,#+."'), ("a", '"=,#+."')) - async def test_is_this_a_macro(self): + def test_is_this_a_macro(self): self.assertTrue(is_this_a_macro("k(1)")) self.assertTrue(is_this_a_macro("k(1).k(2)")) self.assertTrue(is_this_a_macro("r(1, k(1).k(2))")) @@ -252,22 +252,34 @@ class TestMacros(MacroTestBase): self.assertTrue(is_this_a_macro("a + b")) self.assertTrue(is_this_a_macro("a + b + c")) - async def test_handle_plus_syntax(self): - self.assertEqual(handle_plus_syntax("a + b"), "m(a,m(b,h()))") - self.assertEqual(handle_plus_syntax("a + b + c"), "m(a,m(b,m(c,h())))") - self.assertEqual(handle_plus_syntax(" a+b+c "), "m(a,m(b,m(c,h())))") + def test_handle_plus_syntax(self): + self.assertEqual(handle_plus_syntax("a + b"), "modify(a,modify(b,hold()))") + self.assertEqual( + handle_plus_syntax("a + b + c"), "modify(a,modify(b,modify(c,hold())))" + ) + self.assertEqual( + handle_plus_syntax(" a+b+c "), "modify(a,modify(b,modify(c,hold())))" + ) # invalid strings = ["+", "a+", "+b", "k(a + b)"] - for s in strings: + for string in strings: with self.assertRaises(ValueError): - print(f"testing '{s}'") - handle_plus_syntax(s) + logger.info(f'testing "%s"', string) + handle_plus_syntax(string) self.assertEqual(handle_plus_syntax("a"), "a") self.assertEqual(handle_plus_syntax("k(a)"), "k(a)") self.assertEqual(handle_plus_syntax(""), "") + def test_parse_plus_syntax(self): + macro = parse("a + b") + self.assertEqual(macro.code, "modify(a,modify(b,hold()))") + + # this is not erroneously recognized as "plus" syntax + macro = parse("key(a) # a + b") + self.assertEqual(macro.code, "key(a)") + async def test_run_plus_syntax(self): macro = parse("a + b + c + d", self.context) self.assertSetEqual(