Repository: obsidian-tasks-group/obsidian-tasks (ts)

Issue:
Tasks rendered in Reading Mode are not always updated when source file is edited
### Please check that this issue hasn't been reported before.

- [X] I searched previous [Bug Reports](https://github.com/obsidian-tasks-group/obsidian-tasks/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+bug%22+sort%3Acomments-desc) didn't find any similar reports.

### Expected Behavior

_This is initially a placeholder report: I will edit and add more info as I find more out._

**Given** a file is open in both two panes:

- an editing mode (Source or Live Preview)
- Reading mode

**When** I add or remove lines from the file

**Then** rendered tasks in the Reading pane **should always be updated, so that that when I toggle them, the correct line is toggled.**

### Current behaviour

**Given** a file is open in both two panes:

- an editing mode (Source or Live Preview)
- Reading mode

**When** I add or remove lines from the file

**Then** rendered tasks in the Reading pane **are only updated when any tasks in the file are edited**

### Steps to reproduce

### Summary

The steps below make some changes to the Tasks source code so that some internal information is made visible.

Whenever Tasks renders a task, this code will add the following line for debugging purposes:

![image](https://user-images.githubusercontent.com/4840096/220575907-5a6636a5-6d48-46c6-b00f-6dd211a5c022.png)

The information is:

```ts
${task.sectionStart}</b> . ${task.sectionIndex} . '${task.precedingHeader}
```

Making this information visible has revealed that in Reading mode, the rendering of tasks is not updated when lines are added and removed from the source file.

I believe that this is one fundamental cause of Tasks sometimes updating the wrong source line.

### Steps



- Check out and apply the patches - see below
- Build the plugin locally
- Download and add this file to your vault: [Reading Mode line numbers not updated on editing.md](https://github.com/obsidian-tasks-group/obsidian-tasks/files/10801933/Reading.Mode.line.numbers.not.updated.on.editing.md)
- Open the file in both Source mode and Reading mode

Now experiment.

### Initial Display

Initially, the sectionStart and sectionIndex are consistent between rendered tasks and rendered task blocks in Reading mode

![image](https://user-images.githubusercontent.com/4840096/220578958-ff678e49-6a87-43e8-a7a6-7039acd651da.png)

Other things to note:

- The rendered tasks have a null precedingHeader because `InlineRenderer` does not populate that information
- The `task.sectionIndex` is actually the 0-based index of the first task in a section, not the 0-based index of the section heading

### Experiment

Add and remove some lines in the source view, in various locations.

You will see that the position numbers in the tasks block are updated, but the ones in the rendered tasks are not.

![image](https://user-images.githubusercontent.com/4840096/220577792-8d35289c-8c96-4007-bd6e-430a7164761c.png)


### The patches

These are based on code in my fork, at this revision: https://github.com/claremacrae/obsidian-tasks/commit/dfb720eaa5dc16a5a89168dd0fea35b45b061b77

```diff
Index: src/Cache.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/Cache.ts b/src/Cache.ts
--- a/src/Cache.ts	(revision dfb720eaa5dc16a5a89168dd0fea35b45b061b77)
+++ b/src/Cache.ts	(date 1677056378300)
@@ -212,7 +212,8 @@
             // if (this.getState() == State.Warm) {
             //     console.debug(`Tasks unchanged in ${file.path}`);
             // }
-            return;
+            // Disable optimisation that prevents redrawing of Tasks code blocks if tasks are unchanged when a file is updated
+            // return;
         }
 
         if (this.getState() == State.Warm) {
Index: src/InlineRenderer.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/InlineRenderer.ts b/src/InlineRenderer.ts
--- a/src/InlineRenderer.ts	(revision dfb720eaa5dc16a5a89168dd0fea35b45b061b77)
+++ b/src/InlineRenderer.ts	(date 1677051433794)
@@ -69,7 +69,7 @@
                 path,
                 sectionStart: section.lineStart,
                 sectionIndex,
-                precedingHeader: null, // We don't need the preceding header for in-line rendering.
+                precedingHeader: null, // TODO The previous heading would be really useful, to aid in finding the correct task for toggling
                 fallbackDate: null, // We don't need the fallback date for in-line rendering
             });
             if (task !== null) {
Index: src/Query/Query.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/Query/Query.ts b/src/Query/Query.ts
--- a/src/Query/Query.ts	(revision dfb720eaa5dc16a5a89168dd0fea35b45b061b77)
+++ b/src/Query/Query.ts	(date 1677056729091)
@@ -2,7 +2,7 @@
 import type { Task } from '../Task';
 import type { IQuery } from '../IQuery';
 import { getSettings } from '../Config/Settings';
-import { Sort } from './Sort';
+// import { Sort } from './Sort';
 import type { Sorter } from './Sorter';
 import type { TaskGroups } from './TaskGroups';
 import * as FilterParser from './FilterParser';
@@ -134,7 +134,9 @@
             tasks = tasks.filter(filter.filterFunction);
         });
 
-        const tasksSortedLimited = Sort.by(this.sorting, tasks).slice(0, this.limit);
+        // Disabling all sorting, so that tasks are displayed in the order they appear in the file
+        // const tasksSortedLimited = Sort.by(this.sorting, tasks).slice(0, this.limit);
+        const tasksSortedLimited = tasks;
         return Group.by(this.grouping, tasksSortedLimited);
     }
 
Index: src/QueryRenderer.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/QueryRenderer.ts b/src/QueryRenderer.ts
--- a/src/QueryRenderer.ts	(revision dfb720eaa5dc16a5a89168dd0fea35b45b061b77)
+++ b/src/QueryRenderer.ts	(date 1677056729087)
@@ -154,7 +154,8 @@
                 content.appendChild(taskList);
             }
             const totalTasksCount = tasksSortedLimitedGrouped.totalTasksCount();
-            console.debug(`${totalTasksCount} of ${tasks.length} tasks displayed in a block in "${this.filePath}"`);
+            // Disable debug message logging number of tasks displayed in Tasks block
+            // console.debug(`${totalTasksCount} of ${tasks.length} tasks displayed in a block in "${this.filePath}"`);
             this.addTaskCount(content, totalTasksCount);
         } else if (this.query.error !== undefined) {
             content.createDiv().innerHTML =
Index: src/TaskLineRenderer.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/TaskLineRenderer.ts b/src/TaskLineRenderer.ts
--- a/src/TaskLineRenderer.ts	(revision dfb720eaa5dc16a5a89168dd0fea35b45b061b77)
+++ b/src/TaskLineRenderer.ts	(date 1677019042572)
@@ -108,6 +108,7 @@
             taskAsString += componentString;
         }
     }
+    taskAsString += `<br>🐛 <b>${task.sectionStart}</b> . ${task.sectionIndex} . '${task.precedingHeader}'<br>`;
 
     await renderComponentText(parentElement, taskAsString, 'description', task, textRenderer);
 }
Index: src/File.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/File.ts b/src/File.ts
--- a/src/File.ts	(revision dfb720eaa5dc16a5a89168dd0fea35b45b061b77)
+++ b/src/File.ts	(date 1677011294708)
@@ -123,7 +123,8 @@
     let listItem: ListItemCache | undefined;
     let sectionIndex = 0;
     for (const listItemCache of listItemsCache) {
-        if (listItemCache.position.start.line < originalTask.sectionStart) {
+        const lineIndex = listItemCache.position.start.line;
+        if (lineIndex < originalTask.sectionStart) {
             continue;
         }
 
@@ -131,7 +132,7 @@
             continue;
         }
 
-        const line = fileLines[listItemCache.position.start.line];
+        const line = fileLines[lineIndex];
         if (line.includes(globalFilter)) {
             if (sectionIndex === originalTask.sectionIndex) {
                 const dateFromFileName = new Lazy(() => DateFallback.fromPath(originalTask.path));
@@ -151,10 +152,15 @@
 Expected task:
 ${originalTask.originalMarkdown}
 Found task line:
-${line}`;
+${line}
+Other info:
+    previousTries: ${previousTries}
+    originalTask.sectionStart: ${originalTask.sectionStart}
+    originalTask.sectionIndex: ${originalTask.sectionIndex}
+`;
                     console.error(message);
-                    new Notice(message);
-                    return;
+                    new Notice(message, 10000);
+                    return retry();
                 }
                 break;
             }
```

### Which Operating Systems are you using?

- [ ] Android
- [ ] iPhone/iPad
- [ ] Linux
- [X] macOS
- [ ] Windows

### Obsidian Version

1.1.14

### Tasks Plugin Version

1.25.0

### Checks

- [ ] I have tried it with all other plugins disabled and the error still occurs

### Possible solution

I suspect the problem is that `InlineRenderer` should probably use `MarkdownPostProcessorContext.addChild()`

This is based on these instructions:

https://github.com/obsidianmd/obsidian-api/blob/bceb489fc25ceba5973119d6e57759d64850f90d/obsidian.d.ts#L1927-L1936
When a block-referenced task is made complete, it is completed after rewriting the contents of another task.
## Expected Behavior
When a block-referenced task is made complete, the correct task is completed.

## Current Behavior
When a block-referenced task is made complete, it is completed after rewriting the contents of another task.

## Steps to Reproduce
Complete **task2** referenced by **ref task2** with Live Preview of the following Markdown
```
## Category1 
- [ ] task1

## Category 2
- [ ] task2 ^ca47c7

---
- ref task2 ![[#^ca47c7]]
```

1. before
<img width="467" alt="screen1" src="https://user-images.githubusercontent.com/5397279/170285423-038b2009-01d1-4d30-bbc9-a386fa118b6a.png">

2. after
<img width="465" alt="screen2" src="https://user-images.githubusercontent.com/5397279/170284894-7d641bf1-248c-40c5-a993-baba42e5aa91.png">


## Context (Environment)
* Obsidian version: 0.14.13
* Tasks version: 1.5.1
* [x] I have tried it with all other plugins disabled and the error still occurs

## Possible Solution
No idea.

Pull request description:
fix: Major improvements to toggling tasks in Reading view
<!--- Provide a general summary of your changes in the Title above -->

# Description

## tl;dr

Numerous improvements to the behaviour of toggling tasks in Reading view.

- The correct line is found more often.
- And if the correct line cannot be found, a meaningful message is displayed, to give the user chance of fixing the problem.

This started as a spike/experiment to see what I could learn about how `File.replaceTaskWithTasks()` worked. By the end of it, I had made enough improvements that I decided to submit it as-is.

For the cases where a task markdown line appears only once in the file it:

- fixes #688
- fixes #1680

## Gory Detail

- `File.ts`
    - New behaviours added, for searching for a task line within a file:
        - First, use the original task's original line number: If that line in the current file content matches the original markdown, just edit that line - no need to search
        - Second, if there is only one exact, identical copy of the original task's original markdown, use that line - no need to use the unreliable section info
        - If none of those work, fall back on the old section-based info
    - If the task to toggle could not be found, give the user a clear, actionable message (re-open the file & if possible, remove identical, duplicate tasks
    - Add some debug logging (disabled by default)
    - Re-instate Obsidian's own 'File changed externally, merging changes' facility, for when Tasks edits an un-saved file
        - This seems to be working well
        - Fixing this involved:
            1. removing the recently-added auto-save mechanism in `File.ts`
            2. re-instating the original retry() if a line could not be found, perhaps because the cache was out-of-date
- Logging
    - Connect up `logging.ts` so it can now be used
    - Improve formatting and precision of time-stamps in `logging.ts` messages
- Improve a few manual test files in:
    - `resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/`


## Motivation and Context

See above. 

## 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. -->

- Running existing tests
- Updating tests that used to fail and now pass
- Lots of manual testing
- By running the smoke tests. (It was when recently running the smoke tests that I discovered what became #1680)

## Screenshots (if appropriate)

Example error message that is shown if the line to toggle cannot be found:

![image](https://user-images.githubusercontent.com/4840096/226748546-614a66d2-9d9e-4531-a1b5-27bfa78c022c.png)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [x] **Bug fix** (prefix: `fix` - non-breaking change which fixes an issue)
- [x] **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))

Internal changes:

- [x] **Refactor** (prefix: `refactor` - non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [ ] 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

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://publish.obsidian.md/tasks-contributing) and
[Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).
Put an x in the boxes to confirm you agree.
-->

- [x] My contribution follow this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)


Patch:
diff --git a/src/Cache.ts b/src/Cache.ts
index 4930ea55..389a03e4 100644
--- a/src/Cache.ts
+++ b/src/Cache.ts
@@ -9,6 +9,7 @@ import { DateFallback } from './DateFallback';
 import { getSettings } from './Config/Settings';
 import { Lazy } from './lib/Lazy';
 import { TaskLocation } from './TaskLocation';
+// import { logging } from './lib/logging';
 
 export enum State {
     Cold = 'Cold',
@@ -16,6 +17,8 @@ export enum State {
     Warm = 'Warm',
 }
 
+// const logger = logging.getLogger('tasks');
+
 export class Cache {
     private readonly metadataCache: MetadataCache;
     private readonly metadataCacheEventReferences: EventRef[];
@@ -220,6 +223,7 @@ export class Cache {
         }
 
         if (this.getState() == State.Warm) {
+            // logger.debug(`Cache read: ${file.path}`);
             console.debug(
                 `At least one task, its line number or its heading has changed in ${file.path}: triggering a refresh of all active Tasks blocks in Live Preview and Reading mode views.`,
             );
diff --git a/src/File.ts b/src/File.ts
index 2e450749..97883aaa 100644
--- a/src/File.ts
+++ b/src/File.ts
@@ -1,16 +1,17 @@
-import { type ListItemCache, MarkdownView, MetadataCache, Notice, TFile, Vault, Workspace } from 'obsidian';
+import { type ListItemCache, MetadataCache, Notice, TFile, Vault, Workspace } from 'obsidian';
 
 import { getSettings } from './Config/Settings';
 import { type MockListItemCache, type MockTask, saveMockDataForTesting } from './lib/MockDataCreator';
 import type { Task } from './Task';
+import { logging } from './lib/logging';
 
 let metadataCache: MetadataCache | undefined;
 let vault: Vault | undefined;
 let workspace: Workspace | undefined;
 
-/** the two lists below must be maintained together. */
 const supportedFileExtensions = ['md'];
-const supportedViewTypes = [MarkdownView];
+
+const logger = logging.getLogger('tasks');
 
 export type ErrorLoggingFunction = (message: string) => void;
 
@@ -55,6 +56,8 @@ export const replaceTaskWithTasks = async ({
         newTasks = [newTasks];
     }
 
+    logger.debug(`replaceTaskWithTasks entered. ${originalTask.path}`);
+
     tryRepetitive({
         originalTask,
         newTasks,
@@ -67,7 +70,7 @@ export const replaceTaskWithTasks = async ({
 
 function errorAndNotice(message: string) {
     console.error(message);
-    new Notice(message, 10000);
+    new Notice(message, 15000);
 }
 
 function warnAndNotice(message: string) {
@@ -75,6 +78,10 @@ function warnAndNotice(message: string) {
     new Notice(message, 10000);
 }
 
+function debugLog(message: string) {
+    logger.debug(message);
+}
+
 /**
  * This is a workaround to re-try when the returned file cache is `undefined`.
  * Retrying after a while may return a valid file cache.
@@ -95,13 +102,31 @@ const tryRepetitive = async ({
     workspace: Workspace;
     previousTries: number;
 }): Promise<void> => {
+    logger.debug(`tryRepetitive after ${previousTries} previous tries`);
     const retry = () => {
         if (previousTries > 10) {
-            errorAndNotice('Tasks: Too many retries. File update not possible ...');
+            const message = `Tasks: Could not find the correct task line to update.
+
+The task line not updated is:
+${originalTask.originalMarkdown}
+
+In this markdown file:
+"${originalTask.taskLocation.path}"
+
+Note: further clicks on this checkbox will usually now be ignored until the file is opened (or certain, specific edits are made - it's complicated).
+
+Recommendations:
+
+1. Close all panes that have the above file open, and then re-open the file.
+
+2. Check for exactly identical copies of the task line, in this file, and see if you can make them different.
+`;
+            errorAndNotice(message);
             return;
         }
 
         const timeout = Math.min(Math.pow(10, previousTries), 100); // 1, 10, 100, 100, 100, ...
+        logger.debug(`timeout = ${timeout}`);
         setTimeout(() => {
             tryRepetitive({
                 originalTask,
@@ -114,6 +139,9 @@ const tryRepetitive = async ({
         }, timeout);
     };
 
+    // Validate our inputs.
+    // For permanent failures, return nothing.
+    // For failures that might be fixed if we wait for a little while, return retry().
     const file = vault.getAbstractFileByPath(originalTask.path);
     if (!(file instanceof TFile)) {
         warnAndNotice(`Tasks: No file found for task ${originalTask.description}. Retrying ...`);
@@ -137,22 +165,11 @@ const tryRepetitive = async ({
         return retry();
     }
 
-    // before reading the file, save all open views which may contain dirty data not yet saved to filesys.
-    // TODO: future opt is save only if some dirty bit is set.
-    const promises: Promise<void>[] = [];
-    workspace.iterateAllLeaves((leaf) => {
-        supportedViewTypes.forEach((viewType) => {
-            if (leaf.view instanceof viewType && leaf.view.file.path === file.path) {
-                promises.push(leaf.view.save());
-            }
-        });
-    });
-    await Promise.all(promises);
-
+    // We can now try and find which line in the file currently contains originalTask,
+    // so that we know which line to update.
     const fileContent = await vault.read(file); // TODO: replace with vault.process.
     const fileLines = fileContent.split('\n');
-
-    const taskLineNumber = findLineNumberOfTaskToToggle(originalTask, fileLines, listItemsCache, errorAndNotice);
+    const taskLineNumber = findLineNumberOfTaskToToggle(originalTask, fileLines, listItemsCache, debugLog);
 
     if (taskLineNumber === undefined) {
         const logDataForMocking = false;
@@ -163,10 +180,10 @@ const tryRepetitive = async ({
             // to a JSON file and then re-used in a 'unit' test.
             saveMockDataForTesting(originalTask, fileLines, listItemsCache);
         }
-        errorAndNotice('Tasks: could not find task to toggle in the file.');
-        return;
+        return retry();
     }
 
+    // Finally, we can insert 1 or more lines over the original task line:
     const updatedFileLines = [
         ...fileLines.slice(0, taskLineNumber),
         ...newTasks.map((task: Task) => task.toFileLineString()),
@@ -176,6 +193,10 @@ const tryRepetitive = async ({
     await vault.modify(file, updatedFileLines.join('\n'));
 };
 
+function isValidLineNumber(listItemLineNumber: number, fileLines: string[]) {
+    return listItemLineNumber < fileLines.length;
+}
+
 /**
  * Try to find the line number of the originalTask
  * @param originalTask - the {@link Task} line that the user clicked on
@@ -191,14 +212,86 @@ export function findLineNumberOfTaskToToggle(
     fileLines: string[],
     listItemsCache: ListItemCache[] | MockListItemCache[],
     errorLoggingFunction: ErrorLoggingFunction,
+): number | undefined {
+    let result: number | undefined = tryFindingExactMatchAtOriginalLineNumber(originalTask, fileLines);
+    if (result !== undefined) {
+        return result;
+    }
+
+    result = tryFindingIdenticalUniqueMarkdownLineInFile(originalTask, fileLines);
+    if (result !== undefined) {
+        return result;
+    }
+
+    return tryFindingLineNumberFromTaskSectionInfo(originalTask, fileLines, listItemsCache, errorLoggingFunction);
+}
+
+/**
+ *  If the line at line number in originalTask matches original markdown,
+ *  treat that as the correct answer.
+ *
+ *  This could go wrong if:
+ *     - Some lines have been added since originalTask was rendered in Reading view,
+ *       and an identical task line was added, that happened by coincidence to be in the same
+ *       line number as the original task.
+ *
+ * @param originalTask
+ * @param fileLines
+ */
+function tryFindingExactMatchAtOriginalLineNumber(originalTask: Task | MockTask, fileLines: string[]) {
+    const originalTaskLineNumber = originalTask.taskLocation.lineNumber;
+    if (isValidLineNumber(originalTaskLineNumber, fileLines)) {
+        if (fileLines[originalTaskLineNumber] === originalTask.originalMarkdown) {
+            logger.debug(`Found original markdown at original line number ${originalTaskLineNumber}`);
+            return originalTaskLineNumber;
+        }
+    }
+    return undefined;
+}
+
+/**
+ * If the line only appears once in the file, use that line number.
+ *
+ * This could go wrong if:
+ *    - the user had commented out the original task line, and the section had not yet been redrawn
+ * @param originalTask
+ * @param fileLines
+ */
+function tryFindingIdenticalUniqueMarkdownLineInFile(originalTask: Task | MockTask, fileLines: string[]) {
+    const matchingLineNumbers = [];
+    for (let i = 0; i < fileLines.length; i++) {
+        if (fileLines[i] === originalTask.originalMarkdown) {
+            matchingLineNumbers.push(i);
+        }
+    }
+    if (matchingLineNumbers.length === 1) {
+        // There is only one instance of the line in the file, so it must be the
+        // line we are looking for.
+        return matchingLineNumbers[0];
+    }
+    return undefined;
+}
+
+/**
+ * Fall back on the original algorithm, which uses the section information inside the task's {@link TaskLocation}.
+ *
+ * @param originalTask
+ * @param fileLines
+ * @param listItemsCache
+ * @param errorLoggingFunction
+ */
+function tryFindingLineNumberFromTaskSectionInfo(
+    originalTask: Task | MockTask,
+    fileLines: string[],
+    listItemsCache: ListItemCache[] | MockListItemCache[],
+    errorLoggingFunction: ErrorLoggingFunction,
 ) {
-    const fileLinesCount = fileLines.length;
     const { globalFilter } = getSettings();
     let taskLineNumber: number | undefined;
     let sectionIndex = 0;
     for (const listItemCache of listItemsCache) {
         const listItemLineNumber = listItemCache.position.start.line;
-        if (listItemLineNumber >= fileLinesCount) {
+        if (!isValidLineNumber(listItemLineNumber, fileLines)) {
             // One or more lines has been deleted since the cache was populated,
             // so there is at least one list item in the cache that is beyond
             // the end of the actual file on disk.
diff --git a/src/lib/logging.ts b/src/lib/logging.ts
index 8ab332bd..b90bd9dd 100644
--- a/src/lib/logging.ts
+++ b/src/lib/logging.ts
@@ -135,7 +135,7 @@ export class LogManager extends EventEmitter2 {
         if (this.consoleLoggerRegistered) return this;
 
         this.onLogEntry((logEntry) => {
-            let msg = `[${moment().format('YYYYMMDDHHmmss')}][${logEntry.level}][${logEntry.module}]`;
+            let msg = `[${moment().format('YYYY-MM-DD-HH:mm:ss.SSS')}][${logEntry.level}][${logEntry.module}]`;
 
             if (logEntry.traceId) {
                 msg += `[${logEntry.traceId}]`;
diff --git a/src/main.ts b/src/main.ts
index d9a5aca7..c55f403e 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,6 +10,7 @@ import { QueryRenderer } from './QueryRenderer';
 import { getSettings, updateSettings } from './Config/Settings';
 import { SettingsTab } from './Config/SettingsTab';
 import { StatusRegistry } from './StatusRegistry';
+import { logging } from './lib/logging';
 import { EditorSuggestor } from './Suggestor/EditorSuggestorPopup';
 import { StatusSettings } from './Config/StatusSettings';
 import type { Task } from './Task';
@@ -25,6 +26,7 @@ export default class TasksPlugin extends Plugin {
     }
 
     async onload() {
+        logging.registerConsoleLogger();
         console.log('loading plugin "tasks"');
 
         await this.loadSettings();


Test patch:
diff --git a/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/1680 - Reading Mode line numbers not updated on editing.md b/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/1680 - Reading Mode line numbers not updated on editing.md
index c51d564e..0007e362 100644
--- a/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/1680 - Reading Mode line numbers not updated on editing.md
+++ b/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/1680 - Reading Mode line numbers not updated on editing.md
@@ -62,6 +62,20 @@ Some more random tasks:
 
 ## Log of actual results - newest first
 
+Actual result in Tasks 1.26.0
+
+> [!Success]
+>
+> - `Section 2/Task 1` is marked as Done
+> - `Section 2/Task 2` is marked as Done
+
+Note that:
+
+> [!Info]
+> Tasks may write out a harmless message:
+>
+> > "Manual Testing/Task Toggling Scenarios/1680 - Reading Mode line numbers not updated on editing.md" has been modified externally, merging changes automatically.
+
 ### Actual result in `5649417`, PR #1702, 2023-02-27
 
 <https://github.com/obsidian-tasks-group/obsidian-tasks/commit/5649417006a5ccf80cdaaaeb2bc404db5c6f6a86>
diff --git a/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in blockquotes.md b/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in blockquotes.md
index 431f5723..d0e0bd82 100644
--- a/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in blockquotes.md
+++ b/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in blockquotes.md
@@ -9,7 +9,7 @@
 
 **When** I view the tasks in Reading mode with debug enabled
 
-**Then** I see that the callout seems to start a new section line, but the backlinks show the actual ordinary heading name.
+**Then** I see that the blockquote seems to start a new section line, but the backlinks show the actual ordinary heading name.
 
 ## Section 1
 
diff --git a/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in callouts.md b/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in callouts.md
index c055f37b..f48b4415 100644
--- a/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in callouts.md
+++ b/resources/sample_vaults/Tasks-Demo/Manual Testing/Task Toggling Scenarios/Reading Mode line numbers in callouts.md
@@ -9,7 +9,7 @@
 
 **When** I view the tasks in Reading mode with debug enabled
 
-**Then** I see that the blockquote seems to start a new section line, but the backlinks show the actual ordinary heading name.
+**Then** I see that the callout seems to start a new section line, but the backlinks show the actual ordinary heading name.
 
 ## Section 1
 
diff --git a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt
deleted file mode 100644
index 35f6fc08..00000000
--- a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Could not find line for task
diff --git a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt
deleted file mode 100644
index 35f6fc08..00000000
--- a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Could not find line for task
diff --git a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined.approved.txt
new file mode 100644
index 00000000..bd7d320e
--- /dev/null
+++ b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined.approved.txt
@@ -0,0 +1,1 @@
+Success. Line found OK. No error reported.
diff --git a/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task correct behaviour.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task correct behaviour.approved.txt
deleted file mode 100644
index 99f9c454..00000000
--- a/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task correct behaviour.approved.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Tasks: Unable to find task in file Manual Testing/Task Toggling Scenarios/Embed Task in to Note.md.
-Expected task:
-- [ ] #task task2b ^ca47c7
-Found task:
-- [ ] #task task1a
diff --git a/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task current incorrect behaviour.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task current incorrect behaviour.approved.txt
deleted file mode 100644
index 99f9c454..00000000
--- a/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task current incorrect behaviour.approved.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Tasks: Unable to find task in file Manual Testing/Task Toggling Scenarios/Embed Task in to Note.md.
-Expected task:
-- [ ] #task task2b ^ca47c7
-Found task:
-- [ ] #task task1a
diff --git a/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task.approved.txt
new file mode 100644
index 00000000..bd7d320e
--- /dev/null
+++ b/tests/File.test.replaceTaskWithTasks_issue 688 - block referenced task.approved.txt
@@ -0,0 +1,1 @@
+Success. Line found OK. No error reported.
diff --git a/tests/File.test.ts b/tests/File.test.ts
index 25f2ddea..1c041c2f 100644
--- a/tests/File.test.ts
+++ b/tests/File.test.ts
@@ -86,43 +86,22 @@ describe('replaceTaskWithTasks', () => {
 
     // --------------------------------------------------------------------------------
     // Issue 688
-    describe('issue 688 - block referenced task', () => {
+    it('issue 688 - block referenced task', () => {
         const jsonFileName = '688_toggle_block_referenced_line_overwrites_wrong_line.json';
         const taskLineToToggle = '- [ ] #task task2b ^ca47c7';
 
-        it.failing('correct behaviour', () => {
-            // An incorrect line is currently found, so this test fails, due to bug 688
-            const expectedLineNumber = 8;
-            testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber);
-        });
-
-        it('current incorrect behaviour', () => {
-            // An incorrect line is currently found, due to bug 688.
-            // It is recognised as an incorrect line, and so line number is returned as undefined.
-            const expectedLineNumber = undefined;
-            // const actualIncorrectLineFound = '- [ ] #task task1a';
-            testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber);
-        });
+        const expectedLineNumber = 8;
+        testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber);
     });
 
     // --------------------------------------------------------------------------------
     // Issue 1680
-    describe('issue 1680 - Cannot read properties of undefined', () => {
+    it('issue 1680 - Cannot read properties of undefined', () => {
         const jsonFileName = '1680_task_line_number_past_end_of_file.json';
         const taskLineToToggle = '- [ ] #task Section 2/Task 2';
 
-        it.failing('correct behaviour', () => {
-            // An incorrect line is currently found, so this test fails, due to bug 1680
-            const expectedLineNumber = 9;
-            testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber);
-        });
-
-        it('current incorrect behaviour', () => {
-            // An incorrect line is currently found, due to bug 688.
-            // It is recognised as an incorrect line, and so line number is returned as undefined.
-            const expectedLineNumber = undefined;
-            testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber);
-        });
+        const expectedLineNumber = 9;
+        testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber);
     });
 
     // --------------------------------------------------------------------------------