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

Issue:
'sort by tag' should do case-insensitive sorting
### 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

In the Tasks-Demo vault, the following query should show tasks in tag order, regardless of case.
So tags beginning `#context...` should come before `#XX`

````
```tasks
sort by tag
sort by description
tag regex matches /./
```
````

### Current behaviour

This is the results as of Tasks 1.20.0:

![image](https://user-images.githubusercontent.com/4840096/209714652-df766046-050c-4e77-b2ff-27f3a1802c67.png)

Note how `#XX` comes before `#context/...`


### Steps to reproduce

Open the Tasks-Demo vault (see [these notes](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md#how-do-i-test-a-github-build-of-the-tasks-plugin) if you don't know how)
Then view in Reading or Live Preview modes.

### Which Operating Systems are you using?

- [ ] Android
- [ ] iPhone/iPad
- [ ] Linux
- [X] macOS
- [ ] Windows

### Obsidian Version

1.1.9

### Tasks Plugin Version

1.20.0

### Checks

- [ ] I have tried it with all other plugins disabled and the error still occurs

### Possible solution

`TagsField.makeCompareByTagComparator()` needs to use `localeCompare()` - like this code does:

https://github.com/obsidian-tasks-group/obsidian-tasks/blob/a7a64f6e02c80d37f7309a1a9e4ccfab39c442dd/src/Query/Filter/TextField.ts#L94-L105

Pull request:
fix: Make 'sort by tag' ignore capitalisation
<!--- Provide a general summary of your changes in the Title above -->

# Description

Make 'sort by tag' ignore capitalisation

## Motivation and Context

Fixes #1438.

## How has this been tested?

Updating unit tests.
Once this PR has built the plugin, I will test it manually as well.

## Screenshots (if appropriate)


**Before**


![](https://user-images.githubusercontent.com/4840096/209714652-df766046-050c-4e77-b2ff-27f3a1802c67.png)


**After**

![image](https://user-images.githubusercontent.com/4840096/209716780-e81821aa-2d90-463e-ae7d-4f6c60517e2e.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)

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`.
- [ ] My change requires a change to the documentation.
- [ ] 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/src/Query/Filter/TagsField.ts b/src/Query/Filter/TagsField.ts
index 0cb00244..e946da73 100644
--- a/src/Query/Filter/TagsField.ts
+++ b/src/Query/Filter/TagsField.ts
@@ -87,13 +87,7 @@ export class TagsField extends MultiTextField {
                 return 0;
             }
 
-            if (a.tags[tagInstanceToSortBy] < b.tags[tagInstanceToSortBy]) {
-                return -1;
-            } else if (a.tags[tagInstanceToSortBy] > b.tags[tagInstanceToSortBy]) {
-                return 1;
-            } else {
-                return 0;
-            }
+            return a.tags[tagInstanceToSortBy].localeCompare(b.tags[tagInstanceToSortBy]);
         };
     }
 }


Test patch:
diff --git a/tests/Query/Filter/TagsField.test.ts b/tests/Query/Filter/TagsField.test.ts
index 3085d4bf..df4b4710 100644
--- a/tests/Query/Filter/TagsField.test.ts
+++ b/tests/Query/Filter/TagsField.test.ts
@@ -303,6 +303,11 @@ describe('Sort by tags', () => {
         const tags_a_c = new TaskBuilder().tags(['#b', '#c']).build();
         const tags_a_d = new TaskBuilder().tags(['#a', '#d']).build();
 
+        // Helper function to create a task with a given path
+        function with_tags(tags: string[]) {
+            return new TaskBuilder().tags(tags).build();
+        }
+
         it('should create a default comparator, sorting by first tag', () => {
             const comparator = tagsField.comparator();
             expect(comparator(tags_a_d, tags_a_c)).toBeLessThan(0);
@@ -327,6 +332,13 @@ describe('Sort by tags', () => {
             expectTaskComparesAfter(sorter!, tags_a_b, tags_a_c);
         });
 
+        it('should sort by tags case-insensitively', () => {
+            const sorter = tagsField.createNormalSorter();
+            expectTaskComparesBefore(sorter, with_tags(['AAAA']), with_tags(['bbbb']));
+            expectTaskComparesBefore(sorter, with_tags(['aaaa']), with_tags(['BBBB']));
+            expectTaskComparesBefore(sorter, with_tags(['aaaa']), with_tags(['AAAA']));
+        });
+
         it('should fail to parse a invalid line', () => {
             const line = 'sort by jsdajhasdfa';
             expect(tagsField.canCreateSorterForLine(line)).toBe(false);