Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2755 (2024-04-07 21:30:58)

fix: allow Boolean filters such as "description includes abc"
<!--- Provide a general summary of your changes in the Title above -->

# Description

This fixes an unreported bug that whilst single-case Boolean filters like this worked:

```
(description includes abc)
```

ones like this did not:

```
"description includes abc"
```

When I fixed #1170, I only fixed it for `(...)` and not for `"..."`.

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

- Improves the fix for #1170
- And I needed to make the behaviour of `()` and `""` consistent in this area, to simplify later steps in improving Boolean filtering code.

## How has this been tested?

- Unit tests
- Exploratory testing

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [x] **Bug fix** (prefix: `fix` - non-breaking change which fixes an issue)

Internal changes:

- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://publish.obsidian.md/tasks-contributing) and
[Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).
Put an x in the boxes to confirm you agree.
-->

- [x] My contribution follow this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)



Patch:
diff --git a/src/Query/Filter/BooleanDelimiters.ts b/src/Query/Filter/BooleanDelimiters.ts
index 1eb1ad39..bf7ec1e8 100644
--- a/src/Query/Filter/BooleanDelimiters.ts
+++ b/src/Query/Filter/BooleanDelimiters.ts
@@ -11,12 +11,18 @@ export class BooleanDelimiters {
     public readonly openFilterChars;
     public readonly openFilter;
 
-    private constructor(openFilterChars: string) {
+    public readonly closeFilterChars;
+    public readonly closeFilter;
+
+    public constructor(openFilterChars: string, closeFilterChars: string) {
         this.openFilterChars = openFilterChars;
+        this.closeFilterChars = closeFilterChars;
+
         this.openFilter = anyOfTheseChars(this.openFilterChars);
+        this.closeFilter = anyOfTheseChars(this.closeFilterChars);
     }
 
     public static allSupportedDelimiters(): BooleanDelimiters {
-        return new BooleanDelimiters('("');
+        return new BooleanDelimiters('("', ')"');
     }
 }
diff --git a/src/Query/Filter/BooleanField.ts b/src/Query/Filter/BooleanField.ts
index fa9d95fc..1cb3c7f4 100644
--- a/src/Query/Filter/BooleanField.ts
+++ b/src/Query/Filter/BooleanField.ts
@@ -36,9 +36,16 @@ export class BooleanField extends Field {
         // Second pattern matches (filter1) - that is, ensures that a single filter is treated as valid
 
         const delimiters = BooleanDelimiters.allSupportedDelimiters();
-        // This temporarily validates that I have not accidentally changed the expression used in
-        // this.basicBooleanRegexp, by retaining the original hard-coded regular expression for comparison:
-        this.basicBooleanRegexp = new RegExp('(.*(AND|OR|XOR|NOT)\\s*' + delimiters.openFilter + '.*|\\(.+\\))', 'g');
+        this.basicBooleanRegexp = new RegExp(
+            '(.*(AND|OR|XOR|NOT)\\s*' +
+                delimiters.openFilter +
+                '.*|' +
+                delimiters.openFilter +
+                '.+' +
+                delimiters.closeFilter +
+                ')',
+            'g',
+        );
     }
 
     protected filterRegExp(): RegExp {


Test patch:
diff --git a/tests/Query/Filter/BooleanDelimiters.test.ts b/tests/Query/Filter/BooleanDelimiters.test.ts
index de63a426..38f60bba 100644
--- a/tests/Query/Filter/BooleanDelimiters.test.ts
+++ b/tests/Query/Filter/BooleanDelimiters.test.ts
@@ -6,5 +6,8 @@ describe('BooleanDelimiters', () => {
 
         expect(delimiters.openFilterChars).toEqual('("');
         expect(delimiters.openFilter).toEqual('[("]');
+
+        expect(delimiters.closeFilterChars).toEqual(')"');
+        expect(delimiters.closeFilter).toEqual('[)"]');
     });
 });
diff --git a/tests/Query/Filter/BooleanField.test.ts b/tests/Query/Filter/BooleanField.test.ts
index 87f415b4..fba779d1 100644
--- a/tests/Query/Filter/BooleanField.test.ts
+++ b/tests/Query/Filter/BooleanField.test.ts
@@ -168,7 +168,7 @@ describe('boolean query - filter', () => {
         });
 
         describe('" and "', () => {
-            it.failing('should allow " and " as delimiters around 1 filter', () => {
+            it('should allow " and " as delimiters around 1 filter', () => {
                 const filter = createValidFilter('"description includes #context/location1"');
                 expect(explanationOrError(filter)).toMatchInlineSnapshot(`
                     ""description includes #context/location1" =>