Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2848 (2024-05-27 12:27:26)

feat: Store task inheritance
# 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] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
  - Issue/discussion: https://github.com/obsidian-tasks-group/obsidian-tasks/discussions/60

## Description

1. Create class ListItem
2. Task inherits ListItem
3. Original Markdown field pull to ListItem
4. Read nested tasks 

## Motivation and Context

Store task inheritance

## How has this been tested?

Unit tests

## Checklist

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change requires a change to the documentation.
- [ ] I have [updated the documentation](https://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

- [x] My contribution follows 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/Obsidian/Cache.ts b/src/Obsidian/Cache.ts
index 638fee58..32abc4f2 100644
--- a/src/Obsidian/Cache.ts
+++ b/src/Obsidian/Cache.ts
@@ -40,6 +40,7 @@ export function getTasksFromFileContent2(
     // rendered lists.
     let currentSection: SectionCache | null = null;
     let sectionIndex = 0;
+    const line2Task: Map<number, Task> = new Map();
     for (const listItem of listItems) {
         if (listItem.task !== undefined) {
             const lineNumber = listItem.position.start.line;
@@ -91,6 +92,22 @@ export function getTasksFromFileContent2(
                     ),
                     fallbackDate: dateFromFileName.value,
                 });
+
+                if (task !== null) {
+                    // listItem.parent could be negative if the parent is not found (in other words, it is a root task).
+                    // That is not a problem, as we never put a negative number in line2Task map, so parent will be null.
+                    const parentTask: Task | null = line2Task.get(listItem.parent) ?? null;
+                    if (parentTask !== null) {
+                        task = new Task({
+                            ...task,
+                            parent: parentTask,
+                        });
+
+                        parentTask.children.push(task);
+                    }
+
+                    line2Task.set(lineNumber, task);
+                }
             } catch (e) {
                 errorReporter(e, filePath, listItem, line);
                 continue;
diff --git a/src/Task/ListItem.ts b/src/Task/ListItem.ts
index 48e38dd3..95b64978 100644
--- a/src/Task/ListItem.ts
+++ b/src/Task/ListItem.ts
@@ -1,8 +1,15 @@
 export class ListItem {
+    /** The original line read from file.
+     *
+     * Will be empty if Task was created programmatically
+     * (for example, by Create or Edit Task, or in tests, including via {@link TaskBuilder}). */
+    public readonly originalMarkdown: string;
+
     public readonly parent: ListItem | null = null;
     public readonly children: ListItem[];
 
-    constructor(parent: ListItem | null, children: ListItem[]) {
+    constructor(originalMarkdown: string, parent: ListItem | null, children: ListItem[]) {
+        this.originalMarkdown = originalMarkdown;
         this.parent = parent;
         this.children = children;
     }
diff --git a/src/Task/Task.ts b/src/Task/Task.ts
index fa29f7e0..1e0a5024 100644
--- a/src/Task/Task.ts
+++ b/src/Task/Task.ts
@@ -67,12 +67,6 @@ export class Task extends ListItem {
      * Any non-empty value must begin with ' ^'. */
     public readonly blockLink: string;
 
-    /** The original line read from file.
-     *
-     * Will be empty if Task was created programmatically
-     * (for example, by Create or Edit Task, or in tests, including via {@link TaskBuilder}). */
-    public readonly originalMarkdown: string;
-
     public readonly scheduledDateIsInferred: boolean;
 
     private _urgency: number | null = null;
@@ -98,6 +92,7 @@ export class Task extends ListItem {
         tags,
         originalMarkdown,
         scheduledDateIsInferred,
+        parent = null,
     }: {
         // NEW_TASK_FIELD_EDIT_REQUIRED
         status: Status;
@@ -119,8 +114,9 @@ export class Task extends ListItem {
         tags: string[] | [];
         originalMarkdown: string;
         scheduledDateIsInferred: boolean;
+        parent?: ListItem | null;
     }) {
-        super(null, []);
+        super(originalMarkdown, parent, []);
         // NEW_TASK_FIELD_EDIT_REQUIRED
         this.status = status;
         this.description = description;
@@ -145,7 +141,6 @@ export class Task extends ListItem {
         this.id = id;
 
         this.blockLink = blockLink;
-        this.originalMarkdown = originalMarkdown;
 
         this.scheduledDateIsInferred = scheduledDateIsInferred;
     }


Test patch:
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child.md
new file mode 100644
index 00000000..6b9812c8
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child.md
@@ -0,0 +1,2 @@
+- [ ] #task parent
+    - [ ] #task child
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child1newroot_after_header.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child1newroot_after_header.md
new file mode 100644
index 00000000..94aa13b7
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child1newroot_after_header.md
@@ -0,0 +1,8 @@
+# first header
+
+- [ ] #task parent task
+    - [ ] #task child task 1
+
+## second header
+
+- [ ] #task root task
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child1sibling_emptystring.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child1sibling_emptystring.md
new file mode 100644
index 00000000..043eea58
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent1child1sibling_emptystring.md
@@ -0,0 +1,4 @@
+- [ ] #task parent task
+    - [ ] #task child task 1
+
+ - [ ] #task sibling
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children.md
new file mode 100644
index 00000000..cddc12c3
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children.md
@@ -0,0 +1,3 @@
+- [ ] #task parent
+    - [ ] #task child 1
+    - [ ] #task child 2
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children1grandchild.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children1grandchild.md
new file mode 100644
index 00000000..9cc813f4
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children1grandchild.md
@@ -0,0 +1,4 @@
+- [ ] #task parent task
+    - [ ] #task child task 1
+    - [ ] #task child task 2
+        - [ ] #task grandchild 1
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children1sibling.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children1sibling.md
new file mode 100644
index 00000000..f401c114
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children1sibling.md
@@ -0,0 +1,4 @@
+- [ ] #task parent
+    - [ ] #task child 1
+    - [ ] #task child 2
+- [ ] #task sibling
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren.md
new file mode 100644
index 00000000..678dca83
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren.md
@@ -0,0 +1,5 @@
+- [ ] #task parent task
+    - [ ] #task child task 1
+        - [ ] #task grandchild 1
+    - [ ] #task child task 2
+        - [ ] #task grandchild 2
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren1sibling.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren1sibling.md
new file mode 100644
index 00000000..6cde7d24
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren1sibling.md
@@ -0,0 +1,6 @@
+- [ ] #task parent task
+    - [ ] #task child task 1
+        - [ ] #task grandchild 1
+    - [ ] #task child task 2
+        - [ ] #task grandchild 2
+- [ ] #task sibling
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren1sibling_start_with_heading.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren1sibling_start_with_heading.md
new file mode 100644
index 00000000..f79dc2c8
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_1parent2children2grandchildren1sibling_start_with_heading.md
@@ -0,0 +1,8 @@
+# Test heading
+
+- [ ] #task parent task
+    - [ ] #task child task 1
+        - [ ] #task grandchild 1
+    - [ ] #task child task 2
+        - [ ] #task grandchild 2
+- [ ] #task sibling
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_2siblings.md b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_2siblings.md
new file mode 100644
index 00000000..858a5fc3
--- /dev/null
+++ b/resources/sample_vaults/Tasks-Demo/Test Data/inheritance_2siblings.md
@@ -0,0 +1,2 @@
+- [ ] #task sibling 1
+- [ ] #task sibling 2
\ No newline at end of file
diff --git a/tests/Obsidian/Cache.test.ts b/tests/Obsidian/Cache.test.ts
index 9852b6a0..69515fcf 100644
--- a/tests/Obsidian/Cache.test.ts
+++ b/tests/Obsidian/Cache.test.ts
@@ -1,8 +1,25 @@
+/**
+ * @jest-environment jsdom
+ */
+import moment from 'moment/moment';
 import type { CachedMetadata } from 'obsidian';
+import type { Task } from 'Task/Task';
 import { logging } from '../../src/lib/logging';
 import { getTasksFromFileContent2 } from '../../src/Obsidian/Cache';
+import { inheritance_1parent1child } from './__test_data__/inheritance_1parent1child';
+import { inheritance_1parent1child1newroot_after_header } from './__test_data__/inheritance_1parent1child1newroot_after_header';
+import { inheritance_1parent1child1sibling_emptystring } from './__test_data__/inheritance_1parent1child1sibling_emptystring';
+import { inheritance_1parent2children } from './__test_data__/inheritance_1parent2children';
+import { inheritance_1parent2children1grandchild } from './__test_data__/inheritance_1parent2children1grandchild';
+import { inheritance_1parent2children1sibling } from './__test_data__/inheritance_1parent2children1sibling';
+import { inheritance_1parent2children2grandchildren } from './__test_data__/inheritance_1parent2children2grandchildren';
+import { inheritance_1parent2children2grandchildren1sibling } from './__test_data__/inheritance_1parent2children2grandchildren1sibling';
+import { inheritance_1parent2children2grandchildren1sibling_start_with_heading } from './__test_data__/inheritance_1parent2children2grandchildren1sibling_start_with_heading';
+import { inheritance_2siblings } from './__test_data__/inheritance_2siblings';
 import { one_task } from './__test_data__/one_task';
 
+window.moment = moment;
+
 function errorReporter() {
     return;
 }
@@ -52,10 +69,214 @@ function readTasksFromSimulatedFile(testData: SimulatedFile) {
     );
 }
 
+function testRootAndChildren(root: Task, children: Task[]) {
+    expect(root.parent).toEqual(null);
+
+    testChildren(root, children);
+}
+
+function testChildren(parent: Task, childList: Task[]) {
+    expect(parent.children).toEqual(childList);
+    for (const child of childList) {
+        expect(child.parent?.originalMarkdown).toEqual(parent.originalMarkdown);
+        expect(child.parent).toEqual(parent);
+    }
+}
+
 describe('cache', () => {
     it('should read one task', () => {
         const tasks = readTasksFromSimulatedFile(one_task);
         expect(tasks.length).toEqual(1);
         expect(tasks[0].description).toEqual('#task the only task here');
     });
+
+    it('should read two sibling tasks', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_2siblings);
+        expect(inheritance_2siblings.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task sibling 1
+            - [ ] #task sibling 2"
+        `);
+
+        expect(tasks.length).toEqual(2);
+
+        const [sibling1, sibling2] = tasks;
+
+        testRootAndChildren(sibling1, []);
+        testRootAndChildren(sibling2, []);
+    });
+
+    it('should read one parent and one child task', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent1child);
+        expect(inheritance_1parent1child.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent
+                - [ ] #task child"
+        `);
+
+        expect(tasks.length).toEqual(2);
+
+        const [parent, child] = tasks;
+
+        testRootAndChildren(parent, [child]);
+    });
+
+    it('should read one parent and two children task', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent2children);
+        expect(inheritance_1parent2children.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent
+                - [ ] #task child 1
+                - [ ] #task child 2"
+        `);
+
+        expect(tasks.length).toEqual(3);
+
+        const [parent, child1, child2] = tasks;
+
+        testRootAndChildren(parent, [child1, child2]);
+    });
+
+    it('should read one parent, two children and one grandchild', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent2children1grandchild);
+        expect(inheritance_1parent2children1grandchild.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent task
+                - [ ] #task child task 1
+                - [ ] #task child task 2
+                    - [ ] #task grandchild 1
+            "
+        `);
+
+        expect(tasks.length).toEqual(4);
+
+        const [parent, child1, child2, grandchild1] = tasks;
+
+        testRootAndChildren(parent, [child1, child2]);
+        testChildren(child2, [grandchild1]);
+    });
+
+    it('should read one parent, two children and two grandchildren', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent2children2grandchildren);
+        expect(inheritance_1parent2children2grandchildren.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent task
+                - [ ] #task child task 1
+                    - [ ] #task grandchild 1
+                - [ ] #task child task 2
+                    - [ ] #task grandchild 2
+            "
+        `);
+
+        expect(tasks.length).toEqual(5);
+
+        const [parent, child1, grandchild1, child2, grandchild2] = tasks;
+
+        testRootAndChildren(parent, [child1, child2]);
+        testChildren(child1, [grandchild1]);
+        testChildren(child2, [grandchild2]);
+    });
+
+    it('should read one parent, two children, two grandchildren and one sibling', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent2children2grandchildren1sibling);
+        expect(inheritance_1parent2children2grandchildren1sibling.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent task
+                - [ ] #task child task 1
+                    - [ ] #task grandchild 1
+                - [ ] #task child task 2
+                    - [ ] #task grandchild 2
+            - [ ] #task sibling"
+        `);
+
+        expect(tasks.length).toEqual(6);
+
+        const [parent, child1, grandchild1, child2, grandchild2, sibling] = tasks;
+
+        testRootAndChildren(parent, [child1, child2]);
+        testChildren(child1, [grandchild1]);
+        testChildren(child2, [grandchild2]);
+
+        testRootAndChildren(sibling, []);
+    });
+
+    it('should read one parent, 2 children and a sibling', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent2children1sibling);
+        expect(inheritance_1parent2children1sibling.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent
+                - [ ] #task child 1
+                - [ ] #task child 2
+            - [ ] #task sibling"
+        `);
+
+        expect(tasks.length).toEqual(4);
+
+        const [parent, child1, child2, sibling] = tasks;
+
+        testRootAndChildren(parent, [child1, child2]);
+        testRootAndChildren(sibling, []);
+    });
+
+    it('should read sibling separated by empty line', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent1child1sibling_emptystring);
+        expect(inheritance_1parent1child1sibling_emptystring.fileContents).toMatchInlineSnapshot(`
+            "- [ ] #task parent task
+                - [ ] #task child task 1
+
+             - [ ] #task sibling
+            "
+        `);
+
+        expect(tasks.length).toEqual(3);
+
+        const [parent, child, sibling] = tasks;
+
+        testRootAndChildren(parent, [child]);
+        testChildren(child, []);
+
+        testRootAndChildren(sibling, []);
+    });
+
+    it('should read new root task after header', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent1child1newroot_after_header);
+        expect(inheritance_1parent1child1newroot_after_header.fileContents).toMatchInlineSnapshot(`
+            "# first header
+
+            - [ ] #task parent task
+                - [ ] #task child task 1
+
+            ## second header
+
+            - [ ] #task root task
+            "
+        `);
+
+        expect(tasks.length).toEqual(3);
+
+        const [parent, child, newRoot] = tasks;
+
+        testRootAndChildren(parent, [child]);
+        testChildren(child, []);
+
+        testRootAndChildren(newRoot, []);
+    });
+
+    it('should read root on non-starting line', () => {
+        const tasks = readTasksFromSimulatedFile(inheritance_1parent2children2grandchildren1sibling_start_with_heading);
+        expect(inheritance_1parent2children2grandchildren1sibling_start_with_heading.fileContents)
+            .toMatchInlineSnapshot(`
+            "# Test heading
+
+            - [ ] #task parent task
+                - [ ] #task child task 1
+                    - [ ] #task grandchild 1
+                - [ ] #task child task 2
+                    - [ ] #task grandchild 2
+            - [ ] #task sibling"
+        `);
+
+        expect(tasks.length).toEqual(6);
+
+        const [parent, child1, grandchild1, child2, grandchild2, sibling] = tasks;
+
+        testRootAndChildren(parent, [child1, child2]);
+        testChildren(child1, [grandchild1]);
+        testChildren(child2, [grandchild2]);
+
+        testRootAndChildren(sibling, []);
+    });
 });
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent1child.ts b/tests/Obsidian/__test_data__/inheritance_1parent1child.ts
new file mode 100644
index 00000000..4aa9bfeb
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent1child.ts
@@ -0,0 +1,89 @@
+export const inheritance_1parent1child = {
+    filePath: 'Test Data/inheritance_1parent1child.md',
+    fileContents: '- [ ] #task parent\n    - [ ] #task child',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 29,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 34,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 1,
+                        col: 21,
+                        offset: 40,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 18,
+                        offset: 18,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 21,
+                    },
+                    end: {
+                        line: 1,
+                        col: 21,
+                        offset: 40,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent1child1newroot_after_header.ts b/tests/Obsidian/__test_data__/inheritance_1parent1child1newroot_after_header.ts
new file mode 100644
index 00000000..e0a88277
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent1child1newroot_after_header.ts
@@ -0,0 +1,207 @@
+export const inheritance_1parent1child1newroot_after_header = {
+    filePath: 'Test Data/inheritance_1parent1child1newroot_after_header.md',
+    fileContents:
+        '# first header\n' +
+        '\n' +
+        '- [ ] #task parent task\n' +
+        '    - [ ] #task child task 1\n' +
+        '\n' +
+        '## second header\n' +
+        '\n' +
+        '- [ ] #task root task\n',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 6,
+                        offset: 22,
+                    },
+                    end: {
+                        line: 2,
+                        col: 11,
+                        offset: 27,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 10,
+                        offset: 50,
+                    },
+                    end: {
+                        line: 3,
+                        col: 15,
+                        offset: 55,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 7,
+                        col: 6,
+                        offset: 94,
+                    },
+                    end: {
+                        line: 7,
+                        col: 11,
+                        offset: 99,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        headings: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 14,
+                        offset: 14,
+                    },
+                },
+                heading: 'first header',
+                level: 1,
+            },
+            {
+                position: {
+                    start: {
+                        line: 5,
+                        col: 0,
+                        offset: 70,
+                    },
+                    end: {
+                        line: 5,
+                        col: 16,
+                        offset: 86,
+                    },
+                },
+                heading: 'second header',
+                level: 2,
+            },
+        ],
+        sections: [
+            {
+                type: 'heading',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 14,
+                        offset: 14,
+                    },
+                },
+            },
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 2,
+                        col: 0,
+                        offset: 16,
+                    },
+                    end: {
+                        line: 3,
+                        col: 28,
+                        offset: 68,
+                    },
+                },
+            },
+            {
+                type: 'heading',
+                position: {
+                    start: {
+                        line: 5,
+                        col: 0,
+                        offset: 70,
+                    },
+                    end: {
+                        line: 5,
+                        col: 16,
+                        offset: 86,
+                    },
+                },
+            },
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 7,
+                        col: 0,
+                        offset: 88,
+                    },
+                    end: {
+                        line: 7,
+                        col: 21,
+                        offset: 109,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 0,
+                        offset: 16,
+                    },
+                    end: {
+                        line: 2,
+                        col: 23,
+                        offset: 39,
+                    },
+                },
+                parent: -2,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 2,
+                        offset: 42,
+                    },
+                    end: {
+                        line: 3,
+                        col: 28,
+                        offset: 68,
+                    },
+                },
+                parent: 2,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 7,
+                        col: 0,
+                        offset: 88,
+                    },
+                    end: {
+                        line: 7,
+                        col: 21,
+                        offset: 109,
+                    },
+                },
+                parent: -7,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent1child1sibling_emptystring.ts b/tests/Obsidian/__test_data__/inheritance_1parent1child1sibling_emptystring.ts
new file mode 100644
index 00000000..2c371ebc
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent1child1sibling_emptystring.ts
@@ -0,0 +1,120 @@
+export const inheritance_1parent1child1sibling_emptystring = {
+    filePath: 'Test Data/inheritance_1parent1child1sibling_emptystring.md',
+    fileContents: '- [ ] #task parent task\n' + '    - [ ] #task child task 1\n' + '\n' + ' - [ ] #task sibling\n',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 34,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 39,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 7,
+                        offset: 61,
+                    },
+                    end: {
+                        line: 3,
+                        col: 12,
+                        offset: 66,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 3,
+                        col: 20,
+                        offset: 74,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 23,
+                        offset: 23,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 26,
+                    },
+                    end: {
+                        line: 1,
+                        col: 28,
+                        offset: 52,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 0,
+                        offset: 54,
+                    },
+                    end: {
+                        line: 3,
+                        col: 20,
+                        offset: 74,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent2children.ts b/tests/Obsidian/__test_data__/inheritance_1parent2children.ts
new file mode 100644
index 00000000..1e21ed89
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent2children.ts
@@ -0,0 +1,120 @@
+export const inheritance_1parent2children = {
+    filePath: 'Test Data/inheritance_1parent2children.md',
+    fileContents: '- [ ] #task parent\n    - [ ] #task child 1\n    - [ ] #task child 2',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 29,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 34,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 10,
+                        offset: 53,
+                    },
+                    end: {
+                        line: 2,
+                        col: 15,
+                        offset: 58,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 2,
+                        col: 23,
+                        offset: 66,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 18,
+                        offset: 18,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 21,
+                    },
+                    end: {
+                        line: 1,
+                        col: 23,
+                        offset: 42,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 2,
+                        offset: 45,
+                    },
+                    end: {
+                        line: 2,
+                        col: 23,
+                        offset: 66,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent2children1grandchild.ts b/tests/Obsidian/__test_data__/inheritance_1parent2children1grandchild.ts
new file mode 100644
index 00000000..19b77804
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent2children1grandchild.ts
@@ -0,0 +1,155 @@
+export const inheritance_1parent2children1grandchild = {
+    filePath: 'Test Data/inheritance_1parent2children1grandchild.md',
+    fileContents:
+        '- [ ] #task parent task\n' +
+        '    - [ ] #task child task 1\n' +
+        '    - [ ] #task child task 2\n' +
+        '        - [ ] #task grandchild 1\n',
+    cachedMetadata: {
+        tags: [
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 34,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 39,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 2,
+                        col: 10,
+                        offset: 63,
+                    },
+                    end: {
+                        line: 2,
+                        col: 15,
+                        offset: 68,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 3,
+                        col: 14,
+                        offset: 96,
+                    },
+                    end: {
+                        line: 3,
+                        col: 19,
+                        offset: 101,
+                    },
+                },
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 3,
+                        col: 32,
+                        offset: 114,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                parent: -1,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 23,
+                        offset: 23,
+                    },
+                },
+            },
+            {
+                parent: 0,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 26,
+                    },
+                    end: {
+                        line: 1,
+                        col: 28,
+                        offset: 52,
+                    },
+                },
+            },
+            {
+                parent: 0,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 2,
+                        col: 2,
+                        offset: 55,
+                    },
+                    end: {
+                        line: 2,
+                        col: 28,
+                        offset: 81,
+                    },
+                },
+            },
+            {
+                parent: 2,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 3,
+                        col: 6,
+                        offset: 88,
+                    },
+                    end: {
+                        line: 3,
+                        col: 32,
+                        offset: 114,
+                    },
+                },
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent2children1sibling.ts b/tests/Obsidian/__test_data__/inheritance_1parent2children1sibling.ts
new file mode 100644
index 00000000..ec8d51f7
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent2children1sibling.ts
@@ -0,0 +1,152 @@
+export const inheritance_1parent2children1sibling = {
+    filePath: 'Test Data/inheritance_1parent2children1sibling.md',
+    fileContents:
+        '- [ ] #task parent\n' + '    - [ ] #task child 1\n' + '    - [ ] #task child 2\n' + '- [ ] #task sibling',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 29,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 34,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 10,
+                        offset: 53,
+                    },
+                    end: {
+                        line: 2,
+                        col: 15,
+                        offset: 58,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 6,
+                        offset: 73,
+                    },
+                    end: {
+                        line: 3,
+                        col: 11,
+                        offset: 78,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 3,
+                        col: 19,
+                        offset: 86,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 18,
+                        offset: 18,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 21,
+                    },
+                    end: {
+                        line: 1,
+                        col: 23,
+                        offset: 42,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 2,
+                        offset: 45,
+                    },
+                    end: {
+                        line: 2,
+                        col: 23,
+                        offset: 66,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 0,
+                        offset: 67,
+                    },
+                    end: {
+                        line: 3,
+                        col: 19,
+                        offset: 86,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren.ts b/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren.ts
new file mode 100644
index 00000000..004de7d5
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren.ts
@@ -0,0 +1,187 @@
+export const inheritance_1parent2children2grandchildren = {
+    filePath: 'Test Data/inheritance_1parent2children2grandchildren.md',
+    fileContents:
+        '- [ ] #task parent task\n' +
+        '    - [ ] #task child task 1\n' +
+        '        - [ ] #task grandchild 1\n' +
+        '    - [ ] #task child task 2\n' +
+        '        - [ ] #task grandchild 2\n',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 34,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 39,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 14,
+                        offset: 67,
+                    },
+                    end: {
+                        line: 2,
+                        col: 19,
+                        offset: 72,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 10,
+                        offset: 96,
+                    },
+                    end: {
+                        line: 3,
+                        col: 15,
+                        offset: 101,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 4,
+                        col: 14,
+                        offset: 129,
+                    },
+                    end: {
+                        line: 4,
+                        col: 19,
+                        offset: 134,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 4,
+                        col: 32,
+                        offset: 147,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 23,
+                        offset: 23,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 26,
+                    },
+                    end: {
+                        line: 1,
+                        col: 28,
+                        offset: 52,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 6,
+                        offset: 59,
+                    },
+                    end: {
+                        line: 2,
+                        col: 32,
+                        offset: 85,
+                    },
+                },
+                parent: 1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 2,
+                        offset: 88,
+                    },
+                    end: {
+                        line: 3,
+                        col: 28,
+                        offset: 114,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 4,
+                        col: 6,
+                        offset: 121,
+                    },
+                    end: {
+                        line: 4,
+                        col: 32,
+                        offset: 147,
+                    },
+                },
+                parent: 3,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren1sibling.ts b/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren1sibling.ts
new file mode 100644
index 00000000..b49a5de1
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren1sibling.ts
@@ -0,0 +1,219 @@
+export const inheritance_1parent2children2grandchildren1sibling = {
+    filePath: 'Test Data/inheritance_1parent2children2grandchildren1sibling.md',
+    fileContents:
+        '- [ ] #task parent task\n' +
+        '    - [ ] #task child task 1\n' +
+        '        - [ ] #task grandchild 1\n' +
+        '    - [ ] #task child task 2\n' +
+        '        - [ ] #task grandchild 2\n' +
+        '- [ ] #task sibling',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 10,
+                        offset: 34,
+                    },
+                    end: {
+                        line: 1,
+                        col: 15,
+                        offset: 39,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 14,
+                        offset: 67,
+                    },
+                    end: {
+                        line: 2,
+                        col: 19,
+                        offset: 72,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 10,
+                        offset: 96,
+                    },
+                    end: {
+                        line: 3,
+                        col: 15,
+                        offset: 101,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 4,
+                        col: 14,
+                        offset: 129,
+                    },
+                    end: {
+                        line: 4,
+                        col: 19,
+                        offset: 134,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 5,
+                        col: 6,
+                        offset: 154,
+                    },
+                    end: {
+                        line: 5,
+                        col: 11,
+                        offset: 159,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 5,
+                        col: 19,
+                        offset: 167,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 23,
+                        offset: 23,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 2,
+                        offset: 26,
+                    },
+                    end: {
+                        line: 1,
+                        col: 28,
+                        offset: 52,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 2,
+                        col: 6,
+                        offset: 59,
+                    },
+                    end: {
+                        line: 2,
+                        col: 32,
+                        offset: 85,
+                    },
+                },
+                parent: 1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 3,
+                        col: 2,
+                        offset: 88,
+                    },
+                    end: {
+                        line: 3,
+                        col: 28,
+                        offset: 114,
+                    },
+                },
+                parent: 0,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 4,
+                        col: 6,
+                        offset: 121,
+                    },
+                    end: {
+                        line: 4,
+                        col: 32,
+                        offset: 147,
+                    },
+                },
+                parent: 3,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 5,
+                        col: 0,
+                        offset: 148,
+                    },
+                    end: {
+                        line: 5,
+                        col: 19,
+                        offset: 167,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren1sibling_start_with_heading.ts b/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren1sibling_start_with_heading.ts
new file mode 100644
index 00000000..535dafd2
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_1parent2children2grandchildren1sibling_start_with_heading.ts
@@ -0,0 +1,254 @@
+export const inheritance_1parent2children2grandchildren1sibling_start_with_heading = {
+    filePath: 'Test Data/inheritance_1parent2children2grandchildren1sibling_start_with_heading.md',
+    fileContents:
+        '# Test heading\n' +
+        '\n' +
+        '- [ ] #task parent task\n' +
+        '    - [ ] #task child task 1\n' +
+        '        - [ ] #task grandchild 1\n' +
+        '    - [ ] #task child task 2\n' +
+        '        - [ ] #task grandchild 2\n' +
+        '- [ ] #task sibling',
+    cachedMetadata: {
+        tags: [
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 2,
+                        col: 6,
+                        offset: 22,
+                    },
+                    end: {
+                        line: 2,
+                        col: 11,
+                        offset: 27,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 3,
+                        col: 10,
+                        offset: 50,
+                    },
+                    end: {
+                        line: 3,
+                        col: 15,
+                        offset: 55,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 4,
+                        col: 14,
+                        offset: 83,
+                    },
+                    end: {
+                        line: 4,
+                        col: 19,
+                        offset: 88,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 5,
+                        col: 10,
+                        offset: 112,
+                    },
+                    end: {
+                        line: 5,
+                        col: 15,
+                        offset: 117,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 6,
+                        col: 14,
+                        offset: 145,
+                    },
+                    end: {
+                        line: 6,
+                        col: 19,
+                        offset: 150,
+                    },
+                },
+            },
+            {
+                tag: '#task',
+                position: {
+                    start: {
+                        line: 7,
+                        col: 6,
+                        offset: 170,
+                    },
+                    end: {
+                        line: 7,
+                        col: 11,
+                        offset: 175,
+                    },
+                },
+            },
+        ],
+        headings: [
+            {
+                heading: 'Test heading',
+                level: 1,
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 14,
+                        offset: 14,
+                    },
+                },
+            },
+        ],
+        sections: [
+            {
+                type: 'heading',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 14,
+                        offset: 14,
+                    },
+                },
+            },
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 2,
+                        col: 0,
+                        offset: 16,
+                    },
+                    end: {
+                        line: 7,
+                        col: 19,
+                        offset: 183,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                parent: -2,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 2,
+                        col: 0,
+                        offset: 16,
+                    },
+                    end: {
+                        line: 2,
+                        col: 23,
+                        offset: 39,
+                    },
+                },
+            },
+            {
+                parent: 2,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 3,
+                        col: 2,
+                        offset: 42,
+                    },
+                    end: {
+                        line: 3,
+                        col: 28,
+                        offset: 68,
+                    },
+                },
+            },
+            {
+                parent: 3,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 4,
+                        col: 6,
+                        offset: 75,
+                    },
+                    end: {
+                        line: 4,
+                        col: 32,
+                        offset: 101,
+                    },
+                },
+            },
+            {
+                parent: 2,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 5,
+                        col: 2,
+                        offset: 104,
+                    },
+                    end: {
+                        line: 5,
+                        col: 28,
+                        offset: 130,
+                    },
+                },
+            },
+            {
+                parent: 5,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 6,
+                        col: 6,
+                        offset: 137,
+                    },
+                    end: {
+                        line: 6,
+                        col: 32,
+                        offset: 163,
+                    },
+                },
+            },
+            {
+                parent: -2,
+                task: ' ',
+                position: {
+                    start: {
+                        line: 7,
+                        col: 0,
+                        offset: 164,
+                    },
+                    end: {
+                        line: 7,
+                        col: 19,
+                        offset: 183,
+                    },
+                },
+            },
+        ],
+    },
+};
diff --git a/tests/Obsidian/__test_data__/inheritance_2siblings.ts b/tests/Obsidian/__test_data__/inheritance_2siblings.ts
new file mode 100644
index 00000000..fc059922
--- /dev/null
+++ b/tests/Obsidian/__test_data__/inheritance_2siblings.ts
@@ -0,0 +1,89 @@
+export const inheritance_2siblings = {
+    filePath: 'Test Data/inheritance_2siblings.md',
+    fileContents: '- [ ] #task sibling 1\n- [ ] #task sibling 2',
+    cachedMetadata: {
+        tags: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 6,
+                        offset: 6,
+                    },
+                    end: {
+                        line: 0,
+                        col: 11,
+                        offset: 11,
+                    },
+                },
+                tag: '#task',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 6,
+                        offset: 28,
+                    },
+                    end: {
+                        line: 1,
+                        col: 11,
+                        offset: 33,
+                    },
+                },
+                tag: '#task',
+            },
+        ],
+        sections: [
+            {
+                type: 'list',
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 1,
+                        col: 21,
+                        offset: 43,
+                    },
+                },
+            },
+        ],
+        listItems: [
+            {
+                position: {
+                    start: {
+                        line: 0,
+                        col: 0,
+                        offset: 0,
+                    },
+                    end: {
+                        line: 0,
+                        col: 21,
+                        offset: 21,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+            {
+                position: {
+                    start: {
+                        line: 1,
+                        col: 0,
+                        offset: 22,
+                    },
+                    end: {
+                        line: 1,
+                        col: 21,
+                        offset: 43,
+                    },
+                },
+                parent: -1,
+                task: ' ',
+            },
+        ],
+    },
+};
diff --git a/tests/Task/ListItem.test.ts b/tests/Task/ListItem.test.ts
index 26e6ffe3..e35906b9 100644
--- a/tests/Task/ListItem.test.ts
+++ b/tests/Task/ListItem.test.ts
@@ -2,23 +2,23 @@ import { ListItem } from '../../src/Task/ListItem';
 
 describe('list item tests', () => {
     it('should create list item with empty children and absent parent', () => {
-        const listItem = new ListItem(null, []);
+        const listItem = new ListItem('', null, []);
         expect(listItem).toBeDefined();
         expect(listItem.children).toEqual([]);
         expect(listItem.parent).toEqual(null);
     });
 
     it('should create list item with a child', () => {
-        const childItem1 = new ListItem(null, []);
-        const childItem2 = new ListItem(null, []);
-        const listItem = new ListItem(null, [childItem1, childItem2]);
+        const childItem1 = new ListItem('', null, []);
+        const childItem2 = new ListItem('', null, []);
+        const listItem = new ListItem('', null, [childItem1, childItem2]);
         expect(listItem).toBeDefined();
         expect(listItem.children).toEqual([childItem1, childItem2]);
     });
 
     it('should create list item with a parent', () => {
-        const parentItem = new ListItem(null, []);
-        const listItem = new ListItem(parentItem, []);
+        const parentItem = new ListItem('', null, []);
+        const listItem = new ListItem('', parentItem, []);
         expect(listItem).toBeDefined();
         expect(listItem.parent).toEqual(parentItem);
     });