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

Issue:
tag searches do not honour # in query
### Expected Behavior

With this query:

`tags include #home`

I expect only tasks with `#home` to be matched.

So I expect this to be found:

`- [ ] stuff #home` 

And this not to be found:

`- [ ] stuff #location/home`


### Current behaviour

`tags include #home`

matches both of this:

`- [ ] stuff #home` 
`- [ ] stuff #location/home`


### Steps to reproduce

Create a markdown file called `delete me.md` with this content:

````text
# delete me

-  [ ] stuff I should be found #home
 - [ ] stuff I should not be found #location/home

```tasks
not done
tags include #home
path includes delete me.md
```
````

Expected behaviour: Only the first task is matched
Actual behaviour: Both tasks are matched

### Which Operating Systems are you using?

- [ ] Android
- [ ] iPhone/iPad
- [ ] Linux
- [X] macOS
- [ ] Windows

### Obsidian Version

0.15.9

### Tasks Plugin Version

1.12.0

### Checks

- [ ] I have tried it with all other plugins disabled and the error still occurs

### Possible solution

Change this:

```ts
            // Search is done sans the hash. If it is provided then strip it off.
            const search = tagMatch[3].replace(/^#/, '');
```

to this in `TagsField.ts`

```ts
            const search = tagMatch[3];
```

Making this change does not break any of the many tests of tag searching.

Pull request:
fix!!: Tag searches now honour any # character
<!--- Provide a general summary of your changes in the Title above -->

# Description

In `tag` and `tags` searches, if the instruction contains a `#`, it is now honoured.

This means that a search for `tags include #home` no longer matches `- [ ] stuff #location/home`.

### NOTE: Breaking change of undocumented behaviour

If there were any existing searches for `partial_tag_string` like the '#'...

`tag includes #partial_tag_string`

that were intended to match:

`- [ ] some text #blah/partial_tag_string`

Then this change would mean that the above task is no longer found as `#partial_tag_string` is not actually present in the task.

The fix would be to remove the `#` from the query.

### Rationale

I'm taking the view that if the user included a `#` in the query, they intended the `#` to be present.

## Motivation and Context

Fixes #1106

## How has this been tested?

By adding new tests, and confirming that none of the existing tests failed.

## Screenshots (if appropriate)

## 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] **Breaking change** (prefix: `feat!!` or `fix!!` - fix or feature that would cause existing functionality to not work as expected)
- [x] **Documentation** (prefix: `docs` - improvements to any documentation content)

Internal changes:

- [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`.
- [x] My change requires a change to the documentation.
- [x] I have [updated the documentation](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md#updating-documentation) accordingly.
- [x] My change has adequate [Unit Test coverage](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md#maintaining-the-tests).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md) 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://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md)
- [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/filters.md b/docs/queries/filters.md
index 9f07fe37..32596231 100644
--- a/docs/queries/filters.md
+++ b/docs/queries/filters.md
@@ -195,6 +195,7 @@ For example:
   - Matches case-insensitive (disregards capitalization).
   - Disregards the global filter when matching.
   - The `#` is optional on the tag so `#home` and `home` will work to match `#home`.
+  - If the `#` is given, it must be present, so searching for `#home` will match `#home` but not `#location/home`.
   - The match is partial so `tags include foo` will match `#foo/bar` and `#foo-bar`.
 
 #### Tag Query Examples
diff --git a/src/Query/Filter/TagsField.ts b/src/Query/Filter/TagsField.ts
index 3089a14a..0a2bc76f 100644
--- a/src/Query/Filter/TagsField.ts
+++ b/src/Query/Filter/TagsField.ts
@@ -19,9 +19,7 @@ export class TagsField extends Field {
         const tagMatch = Field.getMatch(this.filterRegexp(), line);
         if (tagMatch !== null) {
             const filterMethod = tagMatch[2];
-
-            // Search is done sans the hash. If it is provided then strip it off.
-            const search = tagMatch[3].replace(/^#/, '');
+            const search = tagMatch[3];
 
             if (filterMethod.includes('include')) {
                 const matcher = new SubstringMatcher(search);


Test patch:
diff --git a/tests/Query/Filter/TagsField.test.ts b/tests/Query/Filter/TagsField.test.ts
index 7cf14aa8..6941a629 100644
--- a/tests/Query/Filter/TagsField.test.ts
+++ b/tests/Query/Filter/TagsField.test.ts
@@ -1,8 +1,30 @@
 import { resetSettings, updateSettings } from '../../../src/config/Settings';
 import type { FilteringCase } from '../../TestingTools/FilterTestHelpers';
 import { shouldSupportFiltering } from '../../TestingTools/FilterTestHelpers';
+import { toMatchTaskFromLine } from '../../CustomMatchers/CustomMatchersForFilters';
+import { TagsField } from '../../../src/Query/Filter/TagsField';
+
+expect.extend({
+    toMatchTaskFromLine,
+});
 
 describe('tag/tags', () => {
+    it('should honour any # in query', () => {
+        const filter = new TagsField().createFilterOrErrorMessage(
+            'tags include #home',
+        );
+        expect(filter).toMatchTaskFromLine('- [ ] stuff #home');
+        expect(filter).not.toMatchTaskFromLine('- [ ] stuff #location/home');
+    });
+
+    it('should match any position if no # in query', () => {
+        const filter = new TagsField().createFilterOrErrorMessage(
+            'tags include home',
+        );
+        expect(filter).toMatchTaskFromLine('- [ ] stuff #home');
+        expect(filter).toMatchTaskFromLine('- [ ] stuff #location/home');
+    });
+
     const defaultTasksWithTags = [
         '- [ ] #task something to do #later #work',
         '- [ ] #task something to do #later #work/meeting',