diff --git a/ChangeLog b/ChangeLog index 45b5de443..ceb73d04e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,8 @@ one with apostrophes (thanks to Tomas Babej). - TW-1466 UDA with type duration is stored as text/string (thanks to Thomas Sullivan). +- TW-1472 Syntactic sugar morphs into regex expressions, even if turned off + (thanks to Richard Boß). - TW-1475 task config expands values with unnecessary spaces (thanks to Tomas Babej). - TW-1478 due:easter sets the easter of the current year regardless of the date diff --git a/src/Variant.cpp b/src/Variant.cpp index 2ccb022da..87bed2898 100644 --- a/src/Variant.cpp +++ b/src/Variant.cpp @@ -937,7 +937,25 @@ bool Variant::operator_match (const Variant& other, const Task& task) const } else { - if (find (left._string, pattern, searchCaseSensitive) != std::string::npos) + // If pattern starts with '^', look for a leftmost compare only. + if (pattern[0] == '^' && + find (left._string, + pattern.substr (1), + searchCaseSensitive) == 0) + { + return true; + } + + // If pattern ends with '$', look for a rightmost compare only. + else if (pattern[pattern.length () - 1] == '$' && + find (left._string, + pattern.substr (0, pattern.length () - 1), + searchCaseSensitive) == (left._string.length () - pattern.length () + 1)) + { + return true; + } + + else if (find (left._string, pattern, searchCaseSensitive) != std::string::npos) return true; // If the above did not match, and the left source is "description", then diff --git a/test/search.t b/test/search.t index 7a06a0090..4e1d0e1d2 100755 --- a/test/search.t +++ b/test/search.t @@ -65,7 +65,43 @@ class TestSearch(TestCase): self.assertIn("one", out) self.assertNotIn("two", out) -# TODO Search with rc.regex on and off +class TestBug1472(TestCase): + @classmethod + def setUpClass(cls): + """Executed once before any test in the class""" + cls.t = Task() + cls.t.config("verbose", "nothing") + cls.t("add A to Z") + cls.t("add Z to A") + + def setUp(self): + """Executed before each test in the class""" + + def test_startswith_regex(self): + """Verify .startswith works with regexes""" + code, out, err = self.t("rc.regex:on rc.debug.parser=3 description.startswith:A ls") + self.assertIn("A to Z", out) + self.assertNotIn("Z to A", out) + + def test_endswith_regex(self): + """Verify .endswith works with regexes""" + code, out, err = self.t("rc.regex:on description.endswith:Z ls") + self.assertIn("A to Z", out) + self.assertNotIn("Z to A", out) + + def test_startswith_no_regex(self): + """Verify .startswith works without regexes""" + code, out, err = self.t("rc.regex:off description.startswith:A ls") + self.assertIn("A to Z", out) + self.assertNotIn("Z to A", out) + + def test_endswith_no_regex(self): + """Verify .endswith works without regexes""" + code, out, err = self.t("rc.regex:off description.endswith:Z ls") + self.assertIn("A to Z", out) + self.assertNotIn("Z to A", out) + + # TODO Search with patterns