Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2771 (2024-04-13 20:40:03)

feat: Add Auto Suggest for Task dependencies
<!--- Provide a general summary of your changes in the Title above -->

# Description

<!--- Describe your changes in detail -->

When a task is edited Auto Suggest, now Allows for Adding of ID's and Blocked By ID's.

If a task that blocks another is Choosen via the Auto Suggest, the ID gets Automatically Appended.

## 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. -->
Fixes https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2681

## How has this been tested?

<!--- 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. -->
Simple Test Case were appended.
ID's need to be masked, because their Creation is Random via UUID Algorithm and change Every Time they get suggested.

Please, recheck, but in my understanding only the ordering changed for some reason.

## Screenshots (if appropriate)
![image](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/16257607/766941ab-7bd4-40c3-a6f6-269203f64244)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [ ] **Bug fix** (prefix: `fix` - non-breaking change which fixes an issue)
- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
- [ ] **Breaking change** (prefix: `feat!!` or `fix!!` - fix or feature that would cause existing functionality to not work as expected)
- [ ] **Documentation** (prefix: `docs` - improvements to any documentation content **for users**)
- [ ] **Sample vault** (prefix: `vault` - improvements to the [Tasks-Demo sample vault](https://github.com/obsidian-tasks-group/obsidian-tasks/tree/main/resources/sample_vaults/Tasks-Demo))
- [ ] **Contributing Guidelines** (prefix: `contrib` - any improvements to documentation content **for contributors** - see [Contributing to Tasks](https://publish.obsidian.md/tasks-contributing/))

Internal changes:

- [ ] **Refactor** (prefix: `refactor` - non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
- [ ] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)
- [ ] **Infrastructure** (prefix: `chore` - examples include GitHub Actions, issue templates)

## 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`.
- [ ] My change requires a change to the documentation.
- [ ] I have [updated the documentation](https://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
- [ ] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).
      -> Should have please recheck.

## 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)


claremacrae: Hi @Genei180, thank you very much indeed for working on this.


### Exploratory testing - and what the behaviour is...

I'm unsure from the screenshot exactly how what the intended behaviour of the new code is, or what kinds of scenarios you have tested it with.

Thinking about the kinds of things that would need be covered by exploratory testing, can you kindly record the things that you have done to try and break it - what kind of corner cases and different situations have you tried to break it with, in Obsidian?

### Documentation

Can you at least note here in the ticket a first draft of the bullet points that would need to be covered in the documentation of this new feature?

### My workload

I am sad to say that I am beyond my PR capacity at the moment, with two wide-ranging PRs already open (reminders, and relations between tasks), so I will not be able to give this a thorough review as quickly as I would have liked.

I made a few notes on my first read through the code, though, and I'll add those as a review now, in the hope that you'll be able to answer them whilst this is fresh in your memory...

### Initial observations from a quick play with it...

**How does it determine the order of tasks in the menu?**

![image](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/4840096/c6b0c442-9d47-4e4a-83ef-cda6249d7fe6)


Later: Ohhhh - it defaults to adding a dependency on the previous task, which is really nice!

**It can create repeated dependencies:**

<img width="553" alt="image" src="https://github.com/obsidian-tasks-group/obsidian-tasks/assets/4840096/418e8710-0b0f-4430-9b2b-ae35c081b844">


Which confuses the Edit Task modal:

<img width="446" alt="image" src="https://github.com/obsidian-tasks-group/obsidian-tasks/assets/4840096/7e55c67a-83c6-48b1-9f02-2d175294fd9c">


**I was wondering how it would cope with adding dependencies on tasks in other files**

...  but it seems that it only offers tasks in the file being edited.

I am comfortable with this, as it is a significant simplification - but one that is worth documenting.

**Names of the fields - should be 'depends on', not 'blocked by'**


![image](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/4840096/0384f2f4-7a2f-43b8-886c-e5b2c3d37289)

I just realised that calling it 'blocked by' is a bit misleading... the field name is 'depends on'.

Please see [Search concepts](https://publish.obsidian.md/tasks/Getting+Started/Task+Dependencies#Search+concepts) ...


claremacrae: @Genei180  This is going to be a HUGE usability improvement to the Task Dependencies feature - thanks very much indeed for this work!!
Genei180: > try and break it - what kind of corner cases and different situations have you tried to break it with, in Obsidian?

To be Honest, I'm not experienced in that Field, so non for now.

> Can you at least note here in the ticket a first draft of the bullet points that would need to be covered in the documentation of this new feature?

The Feature, adds to the Auto Suggest, which in turn complements the Task Dependencies Feature.
Nothing Changed in the Task Dependencies Feature, so there should be no Doc Change needed for the Dependencies Feature.

The Auto Suggest should Help the User as Much as Possible, if he needs to look it up in the Documentation, I would say I did Something very wrong.
Additionally, it doesn't change any Behavior at all, except the UI/UX, which should be as Streamlined as Possible, and without Documentation in my Opinion. Should be Self Explanatory.
 
Could you point me in the right Direction, what needs documenting in your Opinion?

> ### My workload
> 
> I am sad to say that I am beyond my PR capacity at the moment, with two wide-ranging PRs already open (reminders, and relations between tasks), so I will not be able to give this a thorough review as quickly as I would have liked.

Don't Worry, I'm out of Time all the Time too :)
A good Review will take its Time.
Please Take as much Time as you need.

> ### Initial observations from a quick play with it...
> 
> **How does it determine the order of tasks in the menu?**

The Behavior is not from me.
It takes Tasks Closer related too it (In Same File, Closer to the Line etc...) first.
The Code for it is in "src\Suggestor\DependencyHelpers.ts" Line: 95.

`results.sort((a, b) => {
            const aInSamePath = a.taskLocation.path === task.taskLocation.path;
            const bInSamePath = b.taskLocation.path === task.taskLocation.path;

            // prioritise tasks close to this task in the same file
            if (aInSamePath && bInSamePath) {
                return (
                    Math.abs(a.taskLocation.lineNumber - task.taskLocation.lineNumber) -
                    Math.abs(b.taskLocation.lineNumber - task.taskLocation.lineNumber)
                );
            } else if (aInSamePath) {
                return -1;
            } else if (bInSamePath) {
                return 1;
            } else {
                return 0;
            }
        });`


> **It can create repeated dependencies:**
> Which confuses the Edit Task modal:

True, should be solved when I add, the Values mentioned by this Comment: https://github.com/obsidian-tasks-group/obsidian-tasks/pull/2771#discussion_r1564245773

> **I was wondering how it would cope with adding dependencies on tasks in other files**
> ... but it seems that it only offers tasks in the file being edited.

For me, it shows tasks in other Files as Well, if i Type a Corresponding Task?
(I just discovered a Bug, where the Last Letter Typed, gets appended too the ID, when the Task is from another File.
Will Look into it)

> I am comfortable with this, as it is a significant simplification - but one that is worth documenting.
Should not be this Way.

> **Names of the fields - should be 'depends on', not 'blocked by'**

- [x] Changed the Naming (Including Multiple Functions Calls)


claremacrae: Wow, thank you @Genei180 for some really helpful comments, which I look forward to reading and replying to when I can give them the time they deserve.

And especially, thank you for these kind comments, which I greatly appreciate.

> Don't Worry, I'm out of Time all the Time too :)
> A good Review will take its Time.
> Please Take as much Time as you need.




Patch:
diff --git a/docs/Editing/Auto-Suggest.md b/docs/Editing/Auto-Suggest.md
index b7d0d485..883f8de7 100644
--- a/docs/Editing/Auto-Suggest.md
+++ b/docs/Editing/Auto-Suggest.md
@@ -192,6 +192,8 @@ Similarly, you can type some fraction of the word `start` (of whatever length is
 | 📅 due date | 📅  |
 | 🛫 start date | 🛫  |
 | ⏳ scheduled date | ⏳  |
+| 🆔 Task ID | 🆔 'Generated ID'  |
+| ⛔ Task depends on ID | ⛔ |
 | ⏫ high priority | ⏫  |
 | 🔼 medium priority | 🔼  |
 | 🔽 low priority | 🔽  |
@@ -288,7 +290,3 @@ at least the specified number of characters to find a match.
 How many suggestions should be shown when an auto-suggest menu pops up (including the "⏎" option).
 
 The default is 6, and you can select any value from 3 to 12.
-
-## Current Limitations
-
-- The Auto-Suggest mechanism does not yet support [[task dependencies]]. We are tracking this in [issue #2681](https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2681).
diff --git a/docs/Getting Started/Task Dependencies.md b/docs/Getting Started/Task Dependencies.md
index c783a481..84052f34 100644
--- a/docs/Getting Started/Task Dependencies.md
+++ b/docs/Getting Started/Task Dependencies.md
@@ -130,6 +130,21 @@ In this scenario, testing with users can only occur after the initial draft is c
 ![Making the 'Test with users' task depend on the 'Build a first draft'](../images/task-dependencies-blocked-by-example.png)
 <span class="caption">Making the **'Test with users'** task depend on the **'Build a first draft'** task.</span>
 
+**Via the Auto Suggest Feature:**
+
+1. (Optional) On the 'Build a frist draft' You can select the ID symbol via the Automatic suggestion dialog box. An ID can be generated automatically via Auto Suggestions. Or you can Specify your own.
+2. select the "Task depends on ID" icon in the "Test with users" task via the Automatic suggestion dialog box.
+3. start entering the task on which it should depend ("Create a first draft")
+4. Press the enter key. If no ID has been entered, an ID is generated for the task.
+
+> [!info]
+> When selecting Task depends on ID:
+>
+> - To depend on multiple tasks, type a comma after the last id in an existing 'depends on' value, and select another task.
+> - it only searche/shows descriptions of not-done tasks.
+> - It initially filters to show tasks in the same file ('closer' to the Task), but if you type more text, it will search all non-done tasks in the vault.
+> - Circular Dependecy, should not be created
+
 By implementing either of these methods, the task list is updated to reflect the dependency relationship:
 
 ```text
diff --git a/docs/Support and Help/Known Limitations.md b/docs/Support and Help/Known Limitations.md
index 4f991e8f..f9a20d5b 100644
--- a/docs/Support and Help/Known Limitations.md
+++ b/docs/Support and Help/Known Limitations.md
@@ -55,10 +55,6 @@ This page gathers together all the documentation on known limitations of the plu
 
 ![[Create or edit Task#Known limitations]]
 
-## Editing Tasks: Auto-Suggest
-
-![[Auto-Suggest#Current Limitations]]
-
 ## Editing Tasks: Postponing
 
 ![[Postponing#Current Limitations]]
diff --git a/resources/sample_vaults/Tasks-Demo/.obsidian/appearance.json b/resources/sample_vaults/Tasks-Demo/.obsidian/appearance.json
index 1a555150..439dd7c7 100644
--- a/resources/sample_vaults/Tasks-Demo/.obsidian/appearance.json
+++ b/resources/sample_vaults/Tasks-Demo/.obsidian/appearance.json
@@ -1,5 +1,5 @@
 {
-  "theme": "moonstone",
+  "theme": "system",
   "enabledCssSnippets": [
     "tasks-code-block-blue-border"
   ],
diff --git a/src/Commands/index.ts b/src/Commands/index.ts
index 3e5a215b..18fade03 100644
--- a/src/Commands/index.ts
+++ b/src/Commands/index.ts
@@ -20,7 +20,7 @@ export class Commands {
             icon: 'pencil',
             editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
                 // TODO Need to explore what happens if a tasks code block is rendered before the Cache has been created.
-                return createOrEdit(checking, editor, view as View, this.app, this.plugin.getTasks()!);
+                return createOrEdit(checking, editor, view as View, this.app, this.plugin.getTasks());
             },
         });
 
diff --git a/src/Suggestor/EditorSuggestorPopup.ts b/src/Suggestor/EditorSuggestorPopup.ts
index b25d1f56..cc7ebe68 100644
--- a/src/Suggestor/EditorSuggestorPopup.ts
+++ b/src/Suggestor/EditorSuggestorPopup.ts
@@ -1,5 +1,8 @@
 import { App, Editor, EditorSuggest, TFile } from 'obsidian';
 import type { EditorPosition, EditorSuggestContext, EditorSuggestTriggerInfo } from 'obsidian';
+import type TasksPlugin from 'main';
+import { ensureTaskHasId } from 'Task/TaskDependency';
+import { replaceTaskWithTasks } from 'Obsidian/File';
 import { type Settings, getUserSelectedTaskFormat } from '../Config/Settings';
 import { canSuggestForLine } from './Suggestor';
 import type { SuggestInfo } from '.';
@@ -10,10 +13,12 @@ export type SuggestInfoWithContext = SuggestInfo & {
 
 export class EditorSuggestor extends EditorSuggest<SuggestInfoWithContext> {
     private settings: Settings;
+    private plugin: TasksPlugin;
 
-    constructor(app: App, settings: Settings) {
+    constructor(app: App, settings: Settings, plugin: TasksPlugin) {
         super(app);
         this.settings = settings;
+        this.plugin = plugin;
 
         // EditorSuggestor swallows tabs while the suggestor popup is open
         // This is a hack to support indenting while popup is open
@@ -48,9 +53,20 @@ export class EditorSuggestor extends EditorSuggest<SuggestInfoWithContext> {
     getSuggestions(context: EditorSuggestContext): SuggestInfoWithContext[] {
         const line = context.query;
         const currentCursor = context.editor.getCursor();
+        const allTasks = this.plugin.getTasks();
+
+        const taskToSuggestFor = allTasks.find(
+            (task) => task.taskLocation.path == context.file.path && task.taskLocation.lineNumber == currentCursor.line,
+        );
 
         const suggestions: SuggestInfo[] =
-            getUserSelectedTaskFormat().buildSuggestions?.(line, currentCursor.ch, this.settings) ?? [];
+            getUserSelectedTaskFormat().buildSuggestions?.(
+                line,
+                currentCursor.ch,
+                this.settings,
+                allTasks,
+                taskToSuggestFor,
+            ) ?? [];
 
         // Add the editor context to all the suggestions
         return suggestions.map((s) => ({ ...s, context }));
@@ -60,7 +76,7 @@ export class EditorSuggestor extends EditorSuggest<SuggestInfoWithContext> {
         el.setText(value.displayText);
     }
 
-    selectSuggestion(value: SuggestInfoWithContext, _evt: MouseEvent | KeyboardEvent) {
+    async selectSuggestion(value: SuggestInfoWithContext, _evt: MouseEvent | KeyboardEvent) {
         const editor = value.context.editor;
 
         if (value.suggestionType === 'empty') {
@@ -73,6 +89,32 @@ export class EditorSuggestor extends EditorSuggest<SuggestInfoWithContext> {
             (editor as any)?.cm?.contentDOM?.dispatchEvent(eventClone);
             return;
         }
+
+        if (value.taskItDependsOn != null) {
+            const newTask = ensureTaskHasId(
+                value.taskItDependsOn,
+                this.plugin.getTasks().map((task) => task.id),
+            );
+            value.appendText += ` ${newTask.id}`;
+
+            if (value.context.file.basename == newTask.filename) {
+                // Avoid "Has Been Modifed Externally Error" and Replace Task in Editor Context
+                console.log(value.taskItDependsOn.toFileLineString());
+                const start = {
+                    line: value.taskItDependsOn.lineNumber,
+                    ch: 0,
+                };
+                const end = {
+                    line: value.taskItDependsOn.lineNumber,
+                    ch: value.taskItDependsOn.toFileLineString().length,
+                };
+                value.context.editor.replaceRange(newTask.toFileLineString(), start, end);
+            } else {
+                // Replace Task in File Context
+                replaceTaskWithTasks({ originalTask: value.taskItDependsOn, newTasks: newTask });
+            }
+        }
+
         const currentCursor = value.context.editor.getCursor();
         const replaceFrom = {
             line: currentCursor.line,
diff --git a/src/Suggestor/Suggestor.ts b/src/Suggestor/Suggestor.ts
index 3429ad7a..d6dcd1d2 100644
--- a/src/Suggestor/Suggestor.ts
+++ b/src/Suggestor/Suggestor.ts
@@ -4,8 +4,10 @@ import { doAutocomplete } from '../lib/DateAbbreviations';
 import { Recurrence } from '../Task/Recurrence';
 import type { DefaultTaskSerializerSymbols } from '../TaskSerializer/DefaultTaskSerializer';
 import { Task } from '../Task/Task';
+import { generateUniqueId } from '../Task/TaskDependency';
 import { GlobalFilter } from '../Config/GlobalFilter';
 import { TaskRegularExpressions } from '../Task/TaskRegularExpressions';
+import { searchForCandidateTasksForDependency } from '../ui/DependencyHelpers';
 import type { SuggestInfo, SuggestionBuilder } from '.';
 
 /**
@@ -24,20 +26,34 @@ export function makeDefaultSuggestionBuilder(
     /*
      * Return a list of suggestions, either generic or more fine-grained to the words at the cursor.
      */
-    return (line: string, cursorPos: number, settings: Settings): SuggestInfo[] => {
+    return (
+        line: string,
+        cursorPos: number,
+        settings: Settings,
+        allTasks: Task[],
+        taskToSuggestFor?: Task,
+    ): SuggestInfo[] => {
         let suggestions: SuggestInfo[] = [];
 
-        // Step 1: add date suggestions if relevant
+        // add date suggestions if relevant
         suggestions = suggestions.concat(
             addDatesSuggestions(line, cursorPos, settings, datePrefixRegex, maxGenericSuggestions, dataviewMode),
         );
 
-        // Step 2: add recurrence suggestions if relevant
+        // add recurrence suggestions if relevant
         suggestions = suggestions.concat(
             addRecurrenceSuggestions(line, cursorPos, settings, symbols.recurrenceSymbol, dataviewMode),
         );
 
-        // Step 3: add task property suggestions ('due', 'recurrence' etc)
+        // add Auto ID suggestions
+        suggestions = suggestions.concat(addIDSuggestion(line, cursorPos, symbols.idSymbol, allTasks));
+
+        // add dependecy suggestions
+        suggestions = suggestions.concat(
+            addDependsOnSuggestions(line, cursorPos, settings, symbols.dependsOnSymbol, allTasks, taskToSuggestFor),
+        );
+
+        // add task property suggestions ('due', 'recurrence' etc)
         suggestions = suggestions.concat(addTaskPropertySuggestions(line, cursorPos, settings, symbols, dataviewMode));
 
         // Unless we have a suggestion that is a match for something the user is currently typing, add
@@ -107,6 +123,19 @@ function addTaskPropertySuggestions(
             displayText: `${symbols.scheduledDateSymbol} scheduled date`,
             appendText: `${symbols.scheduledDateSymbol} `,
         });
+
+    if (!line.includes(symbols.idSymbol))
+        genericSuggestions.push({
+            displayText: `${symbols.idSymbol} Task ID`,
+            appendText: `${symbols.idSymbol}`,
+        });
+
+    if (!line.includes(symbols.dependsOnSymbol))
+        genericSuggestions.push({
+            displayText: `${symbols.dependsOnSymbol} Task depends on ID`,
+            appendText: `${symbols.dependsOnSymbol}`,
+        });
+
     if (!hasPriority(line)) {
         const prioritySymbols: { [key: string]: string } = symbols.prioritySymbols;
         const priorityTexts = ['High', 'Medium', 'Low', 'Highest', 'Lowest'];
@@ -146,7 +175,7 @@ function addTaskPropertySuggestions(
     // something to match, we filter the suggestions accordingly, so the user can get more specific
     // results according to what she's typing.
     // If there's no good match, present the suggestions as they are
-    const wordMatch = matchByPosition(line, /([a-zA-Z'_-]*)/g, cursorPos);
+    const wordMatch = matchIfCursorInRegex(line, /([a-zA-Z'_-]*)/g, cursorPos);
     const matchingSuggestions: SuggestInfo[] = [];
     if (wordMatch && wordMatch.length > 0) {
         const wordUnderCursor = wordMatch[0];
@@ -213,7 +242,7 @@ function addDatesSuggestions(
 
     const results: SuggestInfo[] = [];
     const dateRegex = new RegExp(`(${datePrefixRegex})\\s*([0-9a-zA-Z ]*)`, 'ug');
-    const dateMatch = matchByPosition(line, dateRegex, cursorPos);
+    const dateMatch = matchIfCursorInRegex(line, dateRegex, cursorPos);
     if (dateMatch && dateMatch.length >= 2) {
         const datePrefix = dateMatch[1];
         const dateString = dateMatch[2];
@@ -307,7 +336,7 @@ function addRecurrenceSuggestions(
 
     const results: SuggestInfo[] = [];
     const recurrenceRegex = new RegExp(`(${recurrenceSymbol})\\s*([0-9a-zA-Z ]*)`, 'ug');
-    const recurrenceMatch = matchByPosition(line, recurrenceRegex, cursorPos);
+    const recurrenceMatch = matchIfCursorInRegex(line, recurrenceRegex, cursorPos);
     if (recurrenceMatch && recurrenceMatch.length >= 2) {
         const recurrencePrefix = recurrenceMatch[1];
         const recurrenceString = recurrenceMatch[2];
@@ -378,14 +407,86 @@ function addRecurrenceSuggestions(
     return results;
 }
 
+function addIDSuggestion(line: string, cursorPos: number, idSymbol: string, allTasks: Task[]) {
+    const results: SuggestInfo[] = [];
+    const idRegex = new RegExp(`(${idSymbol})\\s*([0-9a-zA-Z ]*)`, 'ug');
+    const idMatch = matchIfCursorInRegex(line, idRegex, cursorPos);
+
+    if (idMatch && idMatch[0].trim().length <= idSymbol.length) {
+        const ID = generateUniqueId(allTasks.map((task) => task.id));
+        results.push({
+            suggestionType: 'match',
+            displayText: 'Auto Generate Unique ID',
+            appendText: `${idSymbol} ${ID}`,
+            insertAt: idMatch.index,
+            insertSkip: idSymbol.length,
+        });
+    }
+
+    return results;
+}
+
+/*
+ * If the cursor is located in a section that is followed by a Depends On Symbol, suggest options
+ * for what to enter as Depend on Option.
+ * It should contain suggestion of Possible Dependant Tasks
+ * of what the user is typing.
+ */
+function addDependsOnSuggestions(
+    line: string,
+    cursorPos: number,
+    settings: Settings,
+    dependsOnSymbol: string,
+    allTasks: Task[],
+    taskToSuggestFor?: Task,
+) {
+    const results: SuggestInfo[] = [];
+
+    const dependsOnRegex = new RegExp(`(${dependsOnSymbol})([0-9a-zA-Z ^,]*,)*([0-9a-zA-Z ^,]*)`, 'ug');
+    const dependsOnMatch = matchIfCursorInRegex(line, dependsOnRegex, cursorPos);
+    if (dependsOnMatch && dependsOnMatch.length >= 1) {
+        // dependsOnMatch[1] = Depends On Symbol
+        const existingDependsOnIdStrings = dependsOnMatch[2] || '';
+        const newTaskToAppend = dependsOnMatch[3];
+
+        // Find all Tasks, Already Added
+        let blockingTasks = [] as Task[];
+        if (existingDependsOnIdStrings) {
+            blockingTasks = allTasks.filter((task) => task.id && existingDependsOnIdStrings.contains(task.id));
+        }
+
+        if (newTaskToAppend.length >= settings.autoSuggestMinMatch) {
+            const genericMatches = searchForCandidateTasksForDependency(
+                newTaskToAppend.trim(),
+                allTasks,
+                taskToSuggestFor,
+                [] as Task[],
+                blockingTasks,
+            );
+
+            for (const task of genericMatches) {
+                results.push({
+                    suggestionType: 'match',
+                    displayText: `${task.descriptionWithoutTags} - From: ${task.filename}.md`,
+                    appendText: `${dependsOnSymbol}${existingDependsOnIdStrings}`,
+                    insertAt: dependsOnMatch.index,
+                    insertSkip: dependsOnSymbol.length + existingDependsOnIdStrings.length + newTaskToAppend.length,
+                    taskItDependsOn: task,
+                });
+            }
+        }
+    }
+    return results;
+}
+
 /**
  * Matches a string with a regex according to a position (typically of a cursor).
  * Will return a result only if a match exists and the given position is part of it.
  */
-export function matchByPosition(s: string, r: RegExp, position: number): RegExpMatchArray | void {
+export function matchIfCursorInRegex(s: string, r: RegExp, position: number): RegExpMatchArray | void {
     const matches = s.matchAll(r);
     for (const match of matches) {
-        if (match?.index && match.index <= position && position <= match.index + match[0].length) return match;
+        if (match?.index && match.index < position && position <= match.index + match[0].length) return match;
     }
 }
 
@@ -483,11 +584,11 @@ export function lastOpenBracket(
  *   * {@link fn}`(line, cursorPos, settings)` otherwise
  */
 export function onlySuggestIfBracketOpen(fn: SuggestionBuilder, brackets: [string, string][]): SuggestionBuilder {
-    return (line, cursorPos, settings): SuggestInfo[] => {
+    return (line, cursorPos, settings, taskToSuggestFor, allTasks): SuggestInfo[] => {
         if (!isAnyBracketOpen(line.slice(0, cursorPos), brackets)) {
             return [];
         }
-        return fn(line, cursorPos, settings);
+        return fn(line, cursorPos, settings, taskToSuggestFor, allTasks);
     };
 }
 
diff --git a/src/Suggestor/index.ts b/src/Suggestor/index.ts
index b2f198aa..790501fd 100644
--- a/src/Suggestor/index.ts
+++ b/src/Suggestor/index.ts
@@ -1,3 +1,4 @@
+import type { Task } from 'Task/Task';
 import type { Settings } from '../Config/Settings';
 
 /*
@@ -15,9 +16,17 @@ export type SuggestInfo = {
     insertSkip?: number;
     /** Text to match with the user input if matching against the display text is not desirable */
     textToMatch?: string;
+    /** Task which needs too be Updated on Select */
+    taskItDependsOn?: Task;
 };
 
 /*
  * Return a list of suggestions, either generic or more fine-grained to the words at the cursor.
  */
-export type SuggestionBuilder = (line: string, cursorPos: number, settings: Settings) => SuggestInfo[];
+export type SuggestionBuilder = (
+    line: string,
+    cursorPos: number,
+    settings: Settings,
+    allTasks: Task[],
+    taskToSuggestFor?: Task,
+) => SuggestInfo[];
diff --git a/src/main.ts b/src/main.ts
index e7a24284..a9b72006 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,6 @@
 import { Plugin } from 'obsidian';
 
+import type { Task } from 'Task/Task';
 import { Cache } from './Obsidian/Cache';
 import { Commands } from './Commands';
 import { GlobalQuery } from './Config/GlobalQuery';
@@ -14,7 +15,6 @@ import { StatusRegistry } from './Statuses/StatusRegistry';
 import { log, logging } from './lib/logging';
 import { EditorSuggestor } from './Suggestor/EditorSuggestorPopup';
 import { StatusSettings } from './Config/StatusSettings';
-import type { Task } from './Task/Task';
 import { tasksApiV1 } from './Api';
 import { GlobalFilter } from './Config/GlobalFilter';
 
@@ -58,7 +58,7 @@ export default class TasksPlugin extends Plugin {
         this.queryRenderer = new QueryRenderer({ plugin: this, events });
 
         this.registerEditorExtension(newLivePreviewExtension());
-        this.registerEditorSuggest(new EditorSuggestor(this.app, getSettings()));
+        this.registerEditorSuggest(new EditorSuggestor(this.app, getSettings(), this));
         new Commands({ plugin: this });
     }
 
@@ -90,7 +90,11 @@ export default class TasksPlugin extends Plugin {
         await this.saveData(getSettings());
     }
 
-    public getTasks(): Task[] | undefined {
-        return this.cache?.getTasks();
+    public getTasks(): Task[] {
+        if (this.cache === undefined) {
+            return [] as Task[];
+        } else {
+            return this.cache.getTasks();
+        }
     }
 }
diff --git a/src/ui/DependencyHelpers.ts b/src/ui/DependencyHelpers.ts
index f69d099b..1daffdb7 100644
--- a/src/ui/DependencyHelpers.ts
+++ b/src/ui/DependencyHelpers.ts
@@ -54,9 +54,9 @@ function searchDescriptionWithoutTags(query: string, allTasks: Task[]): Task[] {
 export function searchForCandidateTasksForDependency(
     search: string,
     allTasks: Task[],
-    task: Task,
-    blockedBy: Task[],
-    blocking: Task[],
+    task?: Task,
+    blockedBy?: Task[],
+    blocking?: Task[],
 ) {
     let results = searchDescriptionWithoutTags(search, allTasks);
 
@@ -71,36 +71,46 @@ export function searchForCandidateTasksForDependency(
             return false;
         }
 
-        // remove itself, and tasks this task already has a relationship with from results
-        // line number is unavailable for the task being edited
+        // remove itself from results
         // Known issue - filters out duplicate lines in task file
-        const sameFile =
-            item.description === task.description &&
-            item.taskLocation.path === task.taskLocation.path &&
-            item.originalMarkdown === task.originalMarkdown;
-
-        return ![...blockedBy, ...blocking].includes(item) && !sameFile;
-    });
+        const sameTask =
+            item.description === task?.description &&
+            item.taskLocation.path === task?.taskLocation.path &&
+            item.originalMarkdown === task?.originalMarkdown;
+        if (sameTask) {
+            return false;
+        }
 
-    // search results favour tasks from the same file as this task
-    results.sort((a, b) => {
-        const aInSamePath = a.taskLocation.path === task.taskLocation.path;
-        const bInSamePath = b.taskLocation.path === task.taskLocation.path;
-
-        // prioritise tasks close to this task in the same file
-        if (aInSamePath && bInSamePath) {
-            return (
-                Math.abs(a.taskLocation.lineNumber - task.taskLocation.lineNumber) -
-                Math.abs(b.taskLocation.lineNumber - task.taskLocation.lineNumber)
-            );
-        } else if (aInSamePath) {
-            return -1;
-        } else if (bInSamePath) {
-            return 1;
-        } else {
-            return 0;
+        //remove tasks this task already has a relationship with from results
+        if (blockedBy?.includes(item) || blocking?.includes(item)) {
+            return false;
         }
+
+        return true;
     });
 
+    // if a task is provided, show close Relations higher
+    if (task) {
+        // search results favour tasks from the same file as this task
+        results.sort((a, b) => {
+            const aInSamePath = a.taskLocation.path === task.taskLocation.path;
+            const bInSamePath = b.taskLocation.path === task.taskLocation.path;
+
+            // prioritise tasks close to this task in the same file
+            if (aInSamePath && bInSamePath) {
+                return (
+                    Math.abs(a.taskLocation.lineNumber - task.taskLocation.lineNumber) -
+                    Math.abs(b.taskLocation.lineNumber - task.taskLocation.lineNumber)
+                );
+            } else if (aInSamePath) {
+                return -1;
+            } else if (bInSamePath) {
+                return 1;
+            } else {
+                return 0;
+            }
+        });
+    }
+
     return results.slice(0, MAX_SEARCH_RESULTS);
 }


Test patch:
diff --git a/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_offers_basic_completion_options_for_an_empty_task.approved.json b/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_offers_basic_completion_options_for_an_empty_task.approved.json
index dea2870e..6d5ab01c 100644
--- a/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_offers_basic_completion_options_for_an_empty_task.approved.json
+++ b/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_offers_basic_completion_options_for_an_empty_task.approved.json
@@ -12,18 +12,16 @@
     "appendText": "scheduled:: "
   },
   {
-    "displayText": "priority:: high priority",
-    "appendText": "priority:: high] ",
-    "insertSkip": 0
+    "displayText": "id:: Task ID",
+    "appendText": "id::"
   },
   {
-    "displayText": "priority:: medium priority",
-    "appendText": "priority:: medium] ",
-    "insertSkip": 0
+    "displayText": "dependsOn:: Task depends on ID",
+    "appendText": "dependsOn::"
   },
   {
-    "displayText": "priority:: low priority",
-    "appendText": "priority:: low] ",
+    "displayText": "priority:: high priority",
+    "appendText": "priority:: high] ",
     "insertSkip": 0
   }
 ]
diff --git a/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_show_all_suggested_text.approved.md b/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_show_all_suggested_text.approved.md
index c8e5049e..78231066 100644
--- a/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_show_all_suggested_text.approved.md
+++ b/tests/Suggestor/Suggestor.test.auto-complete_with__dataview__symbols_show_all_suggested_text.approved.md
@@ -1,15 +1,7 @@
 | Searchable Text | Text that is added |
 | ----- | ----- |
-| due:: due date | due::  |
-| start:: start date | start::  |
-| scheduled:: scheduled date | scheduled::  |
-| priority:: high priority | priority:: high]  |
-| priority:: medium priority | priority:: medium]  |
-| priority:: low priority | priority:: low]  |
-| priority:: highest priority | priority:: highest]  |
-| priority:: lowest priority | priority:: lowest]  |
-| repeat:: recurring (repeat) | repeat::  |
-| created:: created today (2022-07-11) | created:: 2022-07-11]  |
+| id:: Task ID | id:: |
+| dependsOn:: Task depends on ID | dependsOn:: |
 | every | repeat:: every  |
 | every day | repeat:: every day  |
 | every week | repeat:: every week  |
@@ -23,6 +15,15 @@
 | every week on Thursday | repeat:: every week on Thursday  |
 | every week on Friday | repeat:: every week on Friday  |
 | every week on Saturday | repeat:: every week on Saturday  |
+| due:: due date | due::  |
+| start:: start date | start::  |
+| scheduled:: scheduled date | scheduled::  |
+| priority:: high priority | priority:: high]  |
+| priority:: medium priority | priority:: medium]  |
+| priority:: low priority | priority:: low]  |
+| priority:: highest priority | priority:: highest]  |
+| priority:: lowest priority | priority:: lowest]  |
+| created:: created today (2022-07-11) | created:: 2022-07-11]  |
 | today (2022-07-11) | due:: 2022-07-11]  |
 | tomorrow (2022-07-12) | due:: 2022-07-12]  |
 | Sunday (2022-07-17) | due:: 2022-07-17]  |
@@ -35,6 +36,7 @@
 | next week (2022-07-18) | due:: 2022-07-18]  |
 | next month (2022-08-11) | due:: 2022-08-11]  |
 | next year (2023-07-11) | due:: 2023-07-11]  |
+| repeat:: recurring (repeat) | repeat::  |
 | today (2022-07-11) | scheduled:: 2022-07-11]  |
 | tomorrow (2022-07-12) | scheduled:: 2022-07-12]  |
 | Sunday (2022-07-17) | scheduled:: 2022-07-17]  |
@@ -59,3 +61,4 @@
 | next week (2022-07-18) | start:: 2022-07-18]  |
 | next month (2022-08-11) | start:: 2022-08-11]  |
 | next year (2023-07-11) | start:: 2023-07-11]  |
+| Auto Generate Unique ID | id:: ****** |
diff --git a/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_offers_basic_completion_options_for_an_empty_task.approved.json b/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_offers_basic_completion_options_for_an_empty_task.approved.json
index 10fe4d6b..0e9e2fba 100644
--- a/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_offers_basic_completion_options_for_an_empty_task.approved.json
+++ b/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_offers_basic_completion_options_for_an_empty_task.approved.json
@@ -17,11 +17,11 @@
     "appendText": "⏳ "
   },
   {
-    "displayText": "⏫ high priority",
-    "appendText": "⏫ "
+    "displayText": "🆔 Task ID",
+    "appendText": "🆔"
   },
   {
-    "displayText": "🔼 medium priority",
-    "appendText": "🔼 "
+    "displayText": "⛔ Task depends on ID",
+    "appendText": "⛔"
   }
 ]
diff --git a/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_show_all_suggested_text.approved.md b/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_show_all_suggested_text.approved.md
index 43a501c0..34f63a82 100644
--- a/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_show_all_suggested_text.approved.md
+++ b/tests/Suggestor/Suggestor.test.auto-complete_with__emoji__symbols_show_all_suggested_text.approved.md
@@ -1,16 +1,7 @@
 | Searchable Text | Text that is added |
 | ----- | ----- |
-| ⏎ | &lt;new line> |
-| 📅 due date | 📅  |
-| 🛫 start date | 🛫  |
-| ⏳ scheduled date | ⏳  |
-| ⏫ high priority | ⏫  |
-| 🔼 medium priority | 🔼  |
-| 🔽 low priority | 🔽  |
-| 🔺 highest priority | 🔺  |
-| ⏬ lowest priority | ⏬  |
-| 🔁 recurring (repeat) | 🔁  |
-| ➕ created today (2022-07-11) | ➕ 2022-07-11  |
+| 🆔 Task ID | 🆔 |
+| ⛔ Task depends on ID | ⛔ |
 | every | 🔁 every  |
 | every day | 🔁 every day  |
 | every week | 🔁 every week  |
@@ -24,6 +15,15 @@
 | every week on Thursday | 🔁 every week on Thursday  |
 | every week on Friday | 🔁 every week on Friday  |
 | every week on Saturday | 🔁 every week on Saturday  |
+| 📅 due date | 📅  |
+| 🛫 start date | 🛫  |
+| ⏳ scheduled date | ⏳  |
+| ⏫ high priority | ⏫  |
+| 🔼 medium priority | 🔼  |
+| 🔽 low priority | 🔽  |
+| 🔺 highest priority | 🔺  |
+| ⏬ lowest priority | ⏬  |
+| ➕ created today (2022-07-11) | ➕ 2022-07-11  |
 | today (2022-07-11) | 📅 2022-07-11  |
 | tomorrow (2022-07-12) | 📅 2022-07-12  |
 | Sunday (2022-07-17) | 📅 2022-07-17  |
@@ -36,6 +36,7 @@
 | next week (2022-07-18) | 📅 2022-07-18  |
 | next month (2022-08-11) | 📅 2022-08-11  |
 | next year (2023-07-11) | 📅 2023-07-11  |
+| 🔁 recurring (repeat) | 🔁  |
 | today (2022-07-11) | ⏳ 2022-07-11  |
 | tomorrow (2022-07-12) | ⏳ 2022-07-12  |
 | Sunday (2022-07-17) | ⏳ 2022-07-17  |
@@ -60,3 +61,5 @@
 | next week (2022-07-18) | 🛫 2022-07-18  |
 | next month (2022-08-11) | 🛫 2022-08-11  |
 | next year (2023-07-11) | 🛫 2023-07-11  |
+| Auto Generate Unique ID | 🆔 ****** |
+| ⏎ | &lt;new line> |
diff --git a/tests/Suggestor/Suggestor.test.ts b/tests/Suggestor/Suggestor.test.ts
index 82c025fd..79fe142e 100644
--- a/tests/Suggestor/Suggestor.test.ts
+++ b/tests/Suggestor/Suggestor.test.ts
@@ -4,6 +4,7 @@
 import { verifyAsJson } from 'approvals/lib/Providers/Jest/JestApprovals';
 import moment from 'moment';
 import * as chrono from 'chrono-node';
+import type { Task } from 'Task/Task';
 import { getSettings } from '../../src/Config/Settings';
 import type { SuggestInfo, SuggestionBuilder } from '../../src/Suggestor';
 import {
@@ -17,6 +18,7 @@ import { DATAVIEW_SYMBOLS } from '../../src/TaskSerializer/DataviewTaskSerialize
 import { verifyMarkdown } from '../TestingTools/VerifyMarkdown';
 import { GlobalFilter } from '../../src/Config/GlobalFilter';
 import { MarkdownTable } from '../../src/lib/MarkdownTable';
+import { TaskBuilder } from '../TestingTools/TaskBuilder';
 
 window.moment = moment;
 
@@ -48,6 +50,21 @@ function cursorPosition(line: string): [lineWithoutCursor: string, cursorIndex:
     return [line_without_cursor, line.indexOf('|')];
 }
 
+/**
+ * IDs are randomly generated when a new Task is generated
+ * Too make acceptance Tests not fail Everytime, its masked with a, fixed ID Placeholder: "******"
+ * @param idSymbol - A string that contains the Definition of the ID Symbol
+ * @param suggestions - Suggestion that contain the ID Definition, which should be Replaced
+ * @returns the SuggestInfo, with all ID's Masked
+ */
+function maskIDSuggestionForTesting(idSymbol: string, suggestions: SuggestInfo[]): SuggestInfo[] {
+    const idRegex = new RegExp(`${idSymbol}( [0-9a-zA-Z]*)`, 'ug');
+    suggestions.forEach((element) => {
+        element.appendText = element.appendText.replace(idRegex, `${idSymbol} ******`);
+    });
+    return suggestions;
+}
+
 const MAX_GENERIC_SUGGESTIONS_FOR_TESTS = 50;
 
 // NEW_TASK_FIELD_EDIT_REQUIRED
@@ -62,12 +79,20 @@ describe.each([
         name === 'dataview',
     );
 
-    const { dueDateSymbol, scheduledDateSymbol, startDateSymbol, createdDateSymbol, recurrenceSymbol } = symbols;
+    const {
+        dueDateSymbol,
+        scheduledDateSymbol,
+        startDateSymbol,
+        createdDateSymbol,
+        recurrenceSymbol,
+        idSymbol,
+        dependsOnSymbol,
+    } = symbols;
     it('offers basic completion options for an empty task', () => {
         // Arrange
         const originalSettings = getSettings();
         const line = '- [ ] ';
-        const suggestions: SuggestInfo[] = buildSuggestions(line, 5, originalSettings);
+        const suggestions: SuggestInfo[] = buildSuggestions(line, 5, originalSettings, [] as Task[]);
         verifyAsJson(suggestions);
     });
 
@@ -75,7 +100,7 @@ describe.each([
         // Arrange
         const originalSettings = getSettings();
         const line = `- [ ] some task ${dueDateSymbol}`;
-        const suggestions: SuggestInfo[] = buildSuggestions(line, 17, originalSettings);
+        const suggestions: SuggestInfo[] = buildSuggestions(line, 17, originalSettings, [] as Task[]);
         expect(suggestions[0].displayText).toContain('today');
         expect(suggestions[1].displayText).toContain('tomorrow');
         expect(suggestions.length).toEqual(6);
@@ -85,16 +110,26 @@ describe.each([
         // Arrange
         const originalSettings = getSettings();
         const line = `- [ ] some task ${dueDateSymbol} to`;
-        const suggestions: SuggestInfo[] = buildSuggestions(line, 20, originalSettings);
+        const suggestions: SuggestInfo[] = buildSuggestions(line, 20, originalSettings, [] as Task[]);
         expect(suggestions[0].displayText).toContain('today');
         expect(suggestions[1].displayText).toContain('tomorrow');
     });
 
+    it('offers task suggestions for tasks too possible depend on', () => {
+        // Arrange
+        const originalSettings = getSettings();
+        const line = `- [ ] some task ${dependsOnSymbol} `;
+        const taskToDependOn = TaskBuilder.createFullyPopulatedTask();
+
+        const suggestions: SuggestInfo[] = buildSuggestions(line, line.length - 1, originalSettings, [taskToDependOn]);
+        expect(suggestions[0].displayText).toContain(taskToDependOn.descriptionWithoutTags);
+    });
+
     it('offers generic recurrence completions', () => {
         // Arrange
         const originalSettings = getSettings();
         const line = `- [ ] some task ${recurrenceSymbol}`;
-        const suggestions: SuggestInfo[] = buildSuggestions(line, 17, originalSettings);
+        const suggestions: SuggestInfo[] = buildSuggestions(line, 17, originalSettings, [] as Task[]);
         expect(suggestions[0].displayText).toEqual('every');
         expect(suggestions[1].displayText).toEqual('every day');
         expect(suggestions[2].displayText).toEqual('every week');
@@ -104,7 +139,7 @@ describe.each([
         // Arrange
         const originalSettings = getSettings();
         const line = `- [ ] some task ${recurrenceSymbol} every w`;
-        const suggestions: SuggestInfo[] = buildSuggestions(line, 25, originalSettings);
+        const suggestions: SuggestInfo[] = buildSuggestions(line, 25, originalSettings, [] as Task[]);
         expect(suggestions[0].displayText).toEqual('every week');
         expect(suggestions[1].displayText).toEqual('every week on Sunday');
         expect(suggestions[2].displayText).toEqual('every week on Monday');
@@ -115,10 +150,10 @@ describe.each([
         const originalSettings = getSettings();
         originalSettings.autoSuggestMinMatch = 2;
         let line = `- [ ] some task ${recurrenceSymbol} e`;
-        let suggestions: SuggestInfo[] = buildSuggestions(line, 19, originalSettings);
+        let suggestions: SuggestInfo[] = buildSuggestions(line, 19, originalSettings, [] as Task[]);
         expect(suggestions.length).toEqual(0);
         line = `- [ ] some task ${recurrenceSymbol} ev`;
-        suggestions = buildSuggestions(line, 20, originalSettings);
+        suggestions = buildSuggestions(line, 20, originalSettings, [] as Task[]);
         expect(suggestions[0].displayText).toEqual('every');
         expect(suggestions[1].displayText).toEqual('every day');
     });
@@ -127,11 +162,11 @@ describe.each([
         // Arrange
         const originalSettings = getSettings();
         let line = '- [ ] some task cr';
-        let suggestions: SuggestInfo[] = buildSuggestions(line, 18, originalSettings);
+        let suggestions: SuggestInfo[] = buildSuggestions(line, 18, originalSettings, [] as Task[]);
         expect(suggestions[0].displayText).toEqual(`${createdDateSymbol} created today (2022-07-11)`);
 
         line = '- [ ] some task tod';
-        suggestions = buildSuggestions(line, 19, originalSettings);
+        suggestions = buildSuggestions(line, 19, originalSettings, [] as Task[]);
         if (name === 'emoji') {
             // The first suggestion is new line
             expect(suggestions[0].suggestionType).toEqual('empty');
@@ -156,10 +191,13 @@ describe.each([
             `- [ ] some task ${dueDateSymbol} `,
             `- [ ] some task ${scheduledDateSymbol} `,
             `- [ ] some task ${startDateSymbol} `,
+            `- [ ] some task ${idSymbol} `,
+            `- [ ] some task ${dependsOnSymbol} `,
         ];
         const markdownTable = new MarkdownTable(['Searchable Text', 'Text that is added']);
         for (const line of lines) {
-            const suggestions: SuggestInfo[] = buildSuggestions(line, line.length - 1, originalSettings);
+            let suggestions: SuggestInfo[] = buildSuggestions(line, line.length - 1, originalSettings, [] as Task[]);
+            suggestions = maskIDSuggestionForTesting(idSymbol, suggestions);
             for (const suggestion of suggestions) {
                 // The 'new line' replacement adds a trailing space at the end of a line,
                 // which causes auto-formatting to then make the test fail.
@@ -193,43 +231,43 @@ describe('onlySuggestIfBracketOpen', () => {
 
     it('should suggest if cursor at end of line with an open pair', () => {
         const settings = getSettings();
-        let suggestions = buildSuggestions(...cursorPosition('(hello world|'), settings);
+        let suggestions = buildSuggestions(...cursorPosition('(hello world|'), settings, [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
 
-        suggestions = buildSuggestions(...cursorPosition('[hello world|'), settings);
+        suggestions = buildSuggestions(...cursorPosition('[hello world|'), settings, [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
     });
 
     it('should suggest if cursor at end of line with an nested open pairs', () => {
         const settings = getSettings();
-        let suggestions = buildSuggestions(...cursorPosition('(((hello world))|'), settings);
+        let suggestions = buildSuggestions(...cursorPosition('(((hello world))|'), settings, [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
 
-        suggestions = buildSuggestions(...cursorPosition('[[[hello world]]|'), settings);
+        suggestions = buildSuggestions(...cursorPosition('[[[hello world]]|'), settings, [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
     });
 
     it('should suggest if cursor in middle of closed pair', () => {
         const settings = getSettings();
-        let suggestions = buildSuggestions(...cursorPosition('(hello world|)'), settings);
+        let suggestions = buildSuggestions(...cursorPosition('(hello world|)'), settings, [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
 
-        suggestions = buildSuggestions(...cursorPosition('[hello world|]'), settings);
+        suggestions = buildSuggestions(...cursorPosition('[hello world|]'), settings, [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
     });
 
     it('should suggest if there is an opening bracket after many closing brackets', () => {
-        const suggestions = buildSuggestions(...cursorPosition(']]]]]]](hello|'), getSettings());
+        const suggestions = buildSuggestions(...cursorPosition(']]]]]]](hello|'), getSettings(), [] as Task[]);
         expect(suggestions).not.toEqual(emptySuggestion);
     });
 
     it('should not suggest on an empty line', () => {
-        const suggestions = buildSuggestions(...cursorPosition('|'), getSettings());
+        const suggestions = buildSuggestions(...cursorPosition('|'), getSettings(), [] as Task[]);
         expect(suggestions).toEqual(emptySuggestion);
     });
 
     it("should not suggest if there's no open bracket at cursor position", () => {
-        const suggestions = buildSuggestions(...cursorPosition('(hello world)|'), getSettings());
+        const suggestions = buildSuggestions(...cursorPosition('(hello world)|'), getSettings(), [] as Task[]);
         expect(suggestions).toEqual(emptySuggestion);
     });
 });