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

Issue:
'group by backlink' headings should link to the backlink location
### ⚠️ Please check that this feature request hasn't been suggested before.

- [X] I searched previous [Ideas in Discussions](https://github.com/obsidian-tasks-group/obsidian-tasks/discussions/categories/ideas-any-new-feature-requests-go-in-issues-please?discussions_q=category%3A%22Ideas%3A+Any+New+Feature+Requests+go+in+Issues+please%22+-label%3A%22status%3A+released%22+-label%3Aduplicate+sort%3Atop) didn't find any similar feature requests.
- [X] I searched previous [Issues](https://github.com/obsidian-tasks-group/obsidian-tasks/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+enhancement%22+sort%3Acomments-desc) didn't find any similar feature requests.

### 🔖 Feature description

Thank you for this great tool! My feature request is somewhat related to #473.

I find it very helpful to `group by backlink` and then, to avoid unnecessary duplicate information, `hide backlink`. However, the group header is not hyperlinked. So, the only way to see hover information is to keep showing backlinks *and* have that same information show as the group structure.

Ideally, the group could function as a single backlink, avoiding the repeated information across all tasks.

Thanks!

### ✔️ Solution

Ideally, I could see something like this:
<img width="335" alt="image" src="https://user-images.githubusercontent.com/23620506/219420066-20bcc076-0b29-4de4-a48d-95d93ce26738.png">


Where each group functions as a link instead of just the header and I get the best of both worlds.

This can be enabled by an option like `enable group links`, which tries to make a header into a link where possible.

### ❓ Alternatives

 A more robust solution would be to extend #473 and allow a header format of `[[{{task.backlink}}]]` (or whatever the formatting syntax is), but I have no idea how easy this is to implement or if it plays "nice" with other grouping options.

### 📝 Additional Context

_No response_

Pull request description:
feat: 'group by backlink' headings link to location & show hover previews
# Description

`BacklinkField.grouper()` now generates links to the file/section instead of just a group header.

## Motivation and Context

When grouping by backlink, it's better to `hide backlink` since all the backlinks will be same in a given group. However in this way a clickable backlink is not available anymore. This adds a clickable backlink to the group header.

Fixes #1666:

- #1666

## How has this been tested?

Unit tests updated + smoke test:

query file:

````md
```tasks
tags include backlinktest
hide backlink
group by backlink
```
````

task file:

```md
- [ ] #task #backlinktest no header
# H1 header

- [ ] #task #backlinktest h1

## H2 header

- [ ] #task #backlinktest h2

### H3 header

- [ ] #task #backlinktest h3

###### H6 header under H3

- [ ] #task #backlinktest h6 under h3

#### H4 header

- [ ] #task #backlinktest h4

##### H5 header

- [ ] #task #backlinktest h5

###### H6 header

- [ ] #task #backlinktest h6
```

Group headers now have links:
![Снимок экрана 2023-09-24 в 16 22 40](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/93825870/e2c8b8dc-815f-43b4-a32e-bbba7d7e5f16)

Click on every group header and check that it leads to the clicked section. Example for `- [ ] #task #backlinktest h6 under h3`:
![Снимок экрана 2023-09-24 в 16 24 10](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/93825870/db4818d5-af89-4336-8978-66eb614478b7)
![Снимок экрана 2023-09-24 в 16 24 23](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/93825870/5b3b90c8-a4e6-4bf5-8be0-f6248b7ce3bb)

## Types of changes

Changes visible to users:

- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)

Internal changes:

- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke 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.
- [x] 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 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/docs/Queries/Grouping.md b/docs/Queries/Grouping.md
index 6e0481bf..55f8ffda 100644
--- a/docs/Queries/Grouping.md
+++ b/docs/Queries/Grouping.md
@@ -608,7 +608,7 @@ Since Tasks 4.7.0, the query's file name can be used in custom groups.
 
 ### Backlink
 
-- `group by backlink` (the text that would be shown in the task's [[Backlinks|backlink]], combining the task's file name and heading, but with no link added)
+- `group by backlink` (the text that would be shown in the task's [[Backlinks|backlink]], combining the task's file name and heading, with a link)
 
 ### Heading
 
diff --git a/src/Query/Filter/BacklinkField.ts b/src/Query/Filter/BacklinkField.ts
index b9af03cf..410d3557 100644
--- a/src/Query/Filter/BacklinkField.ts
+++ b/src/Query/Filter/BacklinkField.ts
@@ -1,7 +1,7 @@
 import type { Task } from '../../Task';
 import type { GrouperFunction } from '../Grouper';
-import { TextField } from './TextField';
 import { FilterOrErrorMessage } from './FilterOrErrorMessage';
+import { TextField } from './TextField';
 
 export class BacklinkField extends TextField {
     public fieldName(): string {
@@ -35,15 +35,13 @@ export class BacklinkField extends TextField {
                 return ['Unknown Location'];
             }
 
-            // Always escape the filename, to prevent any underscores being interpreted as markdown:
-            let result = TextField.escapeMarkdownCharacters(filename);
-
-            // Only append the heading if it differs from the un-escaped fileanme:
-            if (task.precedingHeader && task.precedingHeader !== filename) {
-                // We don't escape the heading, as any markdown characters in it really are markdown styling:
-                result += ' > ' + task.precedingHeader;
+            const header = task.precedingHeader;
+            if (header === null) {
+                return ['[[' + filename + ']]'];
             }
-            return [result];
+
+            // Always append the header, to ensure we navigate to the correct section of the file:
+            return [`[[${filename}#${header}|${filename} > ${header}]]`];
         };
     }
 }


Test patch:
diff --git a/tests/Query/Filter/BacklinkField.test.ts b/tests/Query/Filter/BacklinkField.test.ts
index dc89ae55..60da0682 100644
--- a/tests/Query/Filter/BacklinkField.test.ts
+++ b/tests/Query/Filter/BacklinkField.test.ts
@@ -38,22 +38,22 @@ describe('grouping by backlink', () => {
         ['', 'heading', ['Unknown Location']],
 
         // no heading supplied
-        ['a/b/c.md', null, ['c']],
+        ['a/b/c.md', null, ['[[c]]']],
 
         // File and heading, nominal case
-        ['a/b/c.md', 'heading', ['c > heading']],
+        ['a/b/c.md', 'heading', ['[[c#heading|c > heading]]']],
 
-        // If file name and heading are identical, avoid duplication ('c > c')
-        ['a/b/c.md', 'c', ['c']],
+        // If file name and heading are identical, allow duplication ('c > c'), in order to link to correct section
+        ['a/b/c.md', 'c', ['[[c#c|c > c]]']],
 
-        // If file name and heading are identical, avoid duplication, even if there are underscores in the file name
-        ['a_b_c.md', 'a_b_c', ['a\\_b\\_c']],
+        // If file name and heading are identical, allow duplication, even if there are underscores in the file name
+        ['a_b_c.md', 'a_b_c', ['[[a_b_c#a_b_c|a_b_c > a_b_c]]']],
 
-        // Underscores in file name component are escaped
-        ['a/b/_c_.md', null, ['\\_c\\_']],
+        // Underscores in filename component are not escaped
+        ['a/b/_c_.md', null, ['[[_c_]]']],
 
-        // But underscores in the heading component are not
-        ['a/b/_c_.md', 'heading _italic text_', ['\\_c\\_ > heading _italic text_']],
+        // Underscores in the heading component are not escaped either
+        ['a/b/_c_.md', 'heading _italic text_', ['[[_c_#heading _italic text_|_c_ > heading _italic text_]]']],
     ])(
         'path "%s" and heading "%s" should have groups: %s',
         (path: string, heading: string | null, groups: string[]) => {
@@ -73,12 +73,13 @@ describe('grouping by backlink', () => {
 
         // Assert
         expect({ grouper, tasks }).groupHeadingsToBe([
-            '\\_c\\_',
-            '\\_c\\_ > heading _italic text_',
-            'a\\_b\\_c',
-            'b',
-            'c',
-            'c > heading',
+            '[[_c_]]',
+            '[[_c_#heading _italic text_|_c_ > heading _italic text_]]',
+            '[[a_b_c#a_b_c|a_b_c > a_b_c]]',
+            '[[b]]',
+            '[[c]]',
+            '[[c#c|c > c]]',
+            '[[c#heading|c > heading]]',
             'Unknown Location',
         ]);
     });