Repository: obsidian-tasks-group/obsidian-tasks
Pull request #817 (2022-06-21 16:58:27)

refactor: Add ExcludeSubItemsField, for handling 'exclude sub-items' filter
## Description
<!--- Describe your changes in detail -->

1. Add `ExcludeSubItemsField` class
2. Rework Query to use `ExcludeSubItemsField` for ` exclude sub-items` filter
3. Add `ExcludeSubItemsField.test.ts` with simple tests for this new class

## Motivation and Context

More progress on #664 

**This is the last of the filter code in `Query` now split out in to a separate class.**

## How has this been tested?

By running the existing tests, and adding some new ones.

<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how -->
<!--- your change affects other areas of the code, etc. -->

## Screenshots (if appropriate):

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] Refactor (non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

## 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! -->
- [ ] My code follows the code style of this project and passes `yarn run lint`.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] My change has adequate Unit Test coverage.

By creating a Pull Request you agree to our [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md). For further guidance on contributing please see [contributing guide](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md)



Patch:
diff --git a/src/Query.ts b/src/Query.ts
index 5580021d..03e9c1a7 100644
--- a/src/Query.ts
+++ b/src/Query.ts
@@ -10,6 +10,7 @@ import type { Field } from './Query/Filter/Field';
 import { DescriptionField } from './Query/Filter/DescriptionField';
 import { DoneDateField } from './Query/Filter/DoneDateField';
 import { DueDateField } from './Query/Filter/DueDateField';
+import { ExcludeSubItemsField } from './Query/Filter/ExcludeSubItemsField';
 import { HeadingField } from './Query/Filter/HeadingField';
 import { PathField } from './Query/Filter/PathField';
 import { PriorityField } from './Query/Filter/PriorityField';
@@ -73,7 +74,6 @@ export class Query implements IQuery {
     private readonly shortModeRegexp = /^short/;
 
     private readonly limitRegexp = /^limit (to )?(\d+)( tasks?)?/;
-    private readonly excludeSubItemsString = 'exclude sub-items';
 
     private readonly commentRegexp = /^#.*/;
 
@@ -86,9 +86,6 @@ export class Query implements IQuery {
                 switch (true) {
                     case line === '':
                         break;
-                    case line === this.excludeSubItemsString:
-                        this._filters.push((task) => task.indentation === '');
-                        break;
                     case this.shortModeRegexp.test(line):
                         this._layoutOptions.shortMode = true;
                         break;
@@ -116,6 +113,8 @@ export class Query implements IQuery {
                         break;
                     case this.parseFilter(line, new HeadingField()):
                         break;
+                    case this.parseFilter(line, new ExcludeSubItemsField()):
+                        break;
                     case this.limitRegexp.test(line):
                         this.parseLimit({ line });
                         break;
diff --git a/src/Query/Filter/ExcludeSubItemsField.ts b/src/Query/Filter/ExcludeSubItemsField.ts
new file mode 100644
index 00000000..cd401083
--- /dev/null
+++ b/src/Query/Filter/ExcludeSubItemsField.ts
@@ -0,0 +1,35 @@
+import { Field } from './Field';
+import type { FilterOrErrorMessage } from './Filter';
+import { FilterInstructions } from './FilterInstructions';
+
+/**
+ * Implements 'exclude sub-items' filter
+ */
+export class ExcludeSubItemsField extends Field {
+    private readonly _filters = new FilterInstructions();
+
+    constructor() {
+        super();
+
+        this._filters.add(
+            'exclude sub-items',
+            (task) => task.indentation === '',
+        );
+    }
+
+    public canCreateFilterForLine(line: string): boolean {
+        return this._filters.canCreateFilterForLine(line);
+    }
+
+    public createFilterOrErrorMessage(line: string): FilterOrErrorMessage {
+        return this._filters.createFilterOrErrorMessage(line);
+    }
+
+    protected fieldName(): string {
+        return 'exclude';
+    }
+
+    protected filterRegexp(): RegExp | null {
+        return null;
+    }
+}


Test patch:
diff --git a/tests/Query/Filter/ExcludeSubItemsField.test.ts b/tests/Query/Filter/ExcludeSubItemsField.test.ts
new file mode 100644
index 00000000..539b4505
--- /dev/null
+++ b/tests/Query/Filter/ExcludeSubItemsField.test.ts
@@ -0,0 +1,19 @@
+// 'exclude sub-items'
+
+import { testTaskFilter } from '../../TestingTools/FilterTestHelpers';
+import { fromLine } from '../../TestHelpers';
+import { ExcludeSubItemsField } from '../../../src/Query/Filter/ExcludeSubItemsField';
+
+describe('sub-items', () => {
+    it('exclude sub-items', () => {
+        // Arrange
+        const filter = new ExcludeSubItemsField().createFilterOrErrorMessage(
+            'exclude sub-items',
+        );
+
+        // Assert
+        testTaskFilter(filter, fromLine({ line: '- [ ] Task' }), true);
+        testTaskFilter(filter, fromLine({ line: '  - [ ] Subtask1' }), false);
+        testTaskFilter(filter, fromLine({ line: '    - [ ] Subtask2' }), false);
+    });
+});