Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2076 (2023-06-28 13:00:34)

feat: Add `hide tags` instruction
<!--- Provide a general summary of your changes in the Title above -->

# Description

Add instruction to hide all tags in the query

## Motivation and Context

Make query results shorter and more readable.

Partly fixes #848 

## How has this been tested?

New unit tests added.
Smoke test in demo vault.

## 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] **New feature** (prefix: `feat` - non-breaking change which adds functionality)

## 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://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/.github/workflows/update-docs-markdown.yml b/.github/workflows/update-docs-markdown.yml
index bf24a038..acd7af63 100644
--- a/.github/workflows/update-docs-markdown.yml
+++ b/.github/workflows/update-docs-markdown.yml
@@ -12,7 +12,7 @@ jobs:
   docs:
     runs-on: windows-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
 
       - name: Install modules
         run: yarn
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 0d7e5162..d47dc913 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -75,7 +75,7 @@ representative at an online or offline event.
 
 Instances of abusive, harassing, or otherwise unacceptable behavior may be
 reported to the community leaders responsible for enforcement at
-martinschenck@fastmail.com.
+<martinschenck@fastmail.com>.
 All complaints will be reviewed and investigated promptly and fairly.
 
 All community leaders are obligated to respect the privacy and security of the
diff --git a/docs/Advanced/Styling.md b/docs/Advanced/Styling.md
index 3130ced9..7922fc39 100644
--- a/docs/Advanced/Styling.md
+++ b/docs/Advanced/Styling.md
@@ -111,6 +111,11 @@ The CSS `:has` selector is available with Obsidian installer version 1.1.9 and n
 - The query container (`class="plugin-tasks-query-result"`) will include a `tasks-layout-hide...` class, e.g. `tasks-layout-hide-priority`.
 - Although the priority will not be rendered in the query, the upper task element (`li class="task-list-item"`) will still be added the attribute of hidden components, e.g. `data-task-priority="high"`.
 
+> [!warning]
+> For now hiding tags with `hide tags` instruction prevents the highlighting in the task's description.
+>
+> We are tracking this in [issue #....](https://github.com/obsidian-tasks-group/obsidian-tasks/issues/....).
+
 **Short mode** will add a `tasks-layout-short-mode` class to the query container.
 
 **Grouping rules** will add a `data-task-group-by` attribute to the query container, e.g. `data-task-group-by="due,scheduled"`.
diff --git a/docs/Queries/Grouping.md b/docs/Queries/Grouping.md
index 65cb4b58..54bcbf3f 100644
--- a/docs/Queries/Grouping.md
+++ b/docs/Queries/Grouping.md
@@ -44,7 +44,7 @@ To divide the matching tasks up with headings, you can add `group by` lines to t
 
 Tasks provides many built-in grouping options, but sometimes they don't quite do what is wanted by all users.
 
-Now. Tasks has a powerful mechanism for you to create your own **custom groups**, offering incredible flexibility.
+Now Tasks has a powerful mechanism for you to create your own **custom groups**, offering incredible flexibility.
 
 There are many examples of the custom grouping instruction `group by function` in the documentation below, with explanations, for when the `group by` instructions built in to Tasks do not satisfy your preferences.
 
diff --git a/docs/Queries/Layout.md b/docs/Queries/Layout.md
index 9d8b93ab..bf65dfa8 100644
--- a/docs/Queries/Layout.md
+++ b/docs/Queries/Layout.md
@@ -21,6 +21,7 @@ The following elements exist:
 - `due date`
 - `done date`
 - `recurrence rule`
+- `tags`
 - `task count`
 
 > [!released]
@@ -30,8 +31,14 @@ The following elements exist:
 All of these elements except `urgency` are shown by default, so you will use the command `hide`
 if you do not want to show any of them, or the command `show` to show the urgency score.
 
+> [!warning]
+> For now hiding tags with `hide tags` instruction prevents the tag highlighting with CSS styles.
+>
+> We are tracking this in [issue #....](https://github.com/obsidian-tasks-group/obsidian-tasks/issues/....).
+
 > [!released]
-The `show` commands were introduced in Tasks 1.14.0.
+The `show` commands were introduced in Tasks 1.14.0.<br>
+`hide tags` was introduced in Tasks X.Y.Z.
 
 Example:
 
diff --git a/docs/Quick Reference.md b/docs/Quick Reference.md
index 7e2f7e56..ca3eeecc 100644
--- a/docs/Quick Reference.md
+++ b/docs/Quick Reference.md
@@ -38,7 +38,7 @@ This table summarizes the filters and other options available inside a `tasks` b
 |                                                                                                                                                                                                                                                                                                 |                                             | `group by backlink`    | `hide backlink`        |                                              |
 | **[[Filters#Description\|Description]]**, **[[Filters#Tags\|Tags]]** and other odds and ends                                                                                                                                                                                                    |                                             |                        |                        |                                              |
 | `description (includes, does not include) <string>`<br>`description (regex matches, regex does not match) /regex/i`                                                                                                                                                                             | `sort by description`                       |                        |                        | `task.description`                           |
-| `has tags`<br>`no tags`<br>`tag (includes, does not include) <tag>`<br>`tags (include, do not include) <tag>`<br>`tag (regex matches, regex does not match) /regex/i`<br>`tags (regex matches, regex does not match) /regex/i`                                                                  | `sort by tag`<br>`sort by tag <tag_number>` | `group by tags`        |                        | `task.tags`                                  |
+| `has tags`<br>`no tags`<br>`tag (includes, does not include) <tag>`<br>`tags (include, do not include) <tag>`<br>`tag (regex matches, regex does not match) /regex/i`<br>`tags (regex matches, regex does not match) /regex/i`                                                                  | `sort by tag`<br>`sort by tag <tag_number>` | `group by tags`        | `hide tags`            | `task.tags`                                  |
 |                                                                                                                                                                                                                                                                                                 |                                             |                        |                        | `task.originalMarkdown`                      |
 | **[[Combining Filters]]**                                                                                                                                                                                                                                                                       |                                             |                        |                        |                                              |
 | `(filter 1) AND (filter 2)`                                                                                                                                                                                                                                                                     |                                             |                        |                        |                                              |
diff --git a/package.json b/package.json
index fcfac21a..40618ad6 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "eslint-plugin-prettier": "^4.2.1",
     "jest": "^29.5.0",
     "jest-environment-jsdom": "^29.5.0",
-    "markdownlint-cli2": "^0.5.1",
+    "markdownlint-cli2": "^0.8.1",
     "moment": "^2.29.4",
     "obsidian": "^1.1.1",
     "prettier": "^2.2.1",
diff --git a/resources/sample_vaults/Tasks-Demo/Styling/ASCII Tasks Table.md b/resources/sample_vaults/Tasks-Demo/Styling/ASCII Tasks Table.md
index cfdc061f..aa518873 100644
--- a/resources/sample_vaults/Tasks-Demo/Styling/ASCII Tasks Table.md
+++ b/resources/sample_vaults/Tasks-Demo/Styling/ASCII Tasks Table.md
@@ -79,7 +79,15 @@ To:
 - []] #task ASCII code 93 - `]` - box brackets or square brackets, closing bracket
 - [^] #task ASCII code 94 - `^` - Circumflex accent or Caret
 - [_] #task ASCII code 95 - `_` - underscore , understrike , underbar or low line
+
+*Markdown-linting [rule MD038](https://github.com/DavidAnson/markdownlint/blob/main/doc/md038.md) disabled here, inside comments:*
+
+<!-- markdownlint-disable MD038 -->
 - [`] #task ASCII code 96 - `backtick` - Grave accent
+<!-- markdownlint-enable MD038 -->
+
+*Markdown-linting rule MD038 now turned back on.*
+
 - [a] #task ASCII code 97 - `a` - Lowercase letter a , minuscule a
 - [b] #task ASCII code 98 - `b` - Lowercase letter b , minuscule b
 - [c] #task ASCII code 99 - `c` - Lowercase letter c , minuscule c
diff --git a/src/Query/Query.ts b/src/Query/Query.ts
index 97bebc4b..dcbb48c0 100644
--- a/src/Query/Query.ts
+++ b/src/Query/Query.ts
@@ -22,7 +22,7 @@ export class Query implements IQuery {
     private _grouping: Grouper[] = [];
 
     private readonly hideOptionsRegexp =
-        /^(hide|show) (task count|backlink|priority|created date|start date|scheduled date|done date|due date|recurrence rule|edit button|urgency)/;
+        /^(hide|show) (task count|backlink|priority|created date|start date|scheduled date|done date|due date|recurrence rule|edit button|urgency|tags)/;
     private readonly shortModeRegexp = /^short/;
     private readonly explainQueryRegexp = /^explain/;
 
@@ -229,6 +229,9 @@ export class Query implements IQuery {
                 case 'urgency':
                     this._layoutOptions.hideUrgency = hide;
                     break;
+                case 'tags':
+                    this._layoutOptions.hideTags = hide;
+                    break;
                 default:
                     this._error = 'do not understand hide/show option';
             }
diff --git a/src/TaskLayout.ts b/src/TaskLayout.ts
index 2597e7ec..9e0ee2bf 100644
--- a/src/TaskLayout.ts
+++ b/src/TaskLayout.ts
@@ -14,6 +14,7 @@ export class LayoutOptions {
     hideRecurrenceRule: boolean = false;
     hideEditButton: boolean = false;
     hideUrgency: boolean = true;
+    hideTags: boolean = false;
     shortMode: boolean = false;
     explainQuery: boolean = false;
 }
diff --git a/src/TaskLineRenderer.ts b/src/TaskLineRenderer.ts
index 100ca77b..1379c0a7 100644
--- a/src/TaskLineRenderer.ts
+++ b/src/TaskLineRenderer.ts
@@ -1,6 +1,6 @@
 import { Component, MarkdownRenderer } from 'obsidian';
 import type { Moment } from 'moment';
-import type { Task } from './Task';
+import { type Task, TaskRegularExpressions } from './Task';
 import * as taskModule from './Task';
 import type { LayoutOptions, TaskLayoutComponent } from './TaskLayout';
 import { TaskLayout } from './TaskLayout';
@@ -140,6 +140,10 @@ async function taskToHtml(
         if (componentString) {
             if (component === 'description') {
                 componentString = GlobalFilter.removeAsWordFromDependingOnSettings(componentString);
+
+                if (renderDetails.layoutOptions?.hideTags) {
+                    componentString = componentString.replace(TaskRegularExpressions.hashTags, '').trim();
+                }
             }
             // Create the text span that will hold the rendered component
             const span = document.createElement('span');
diff --git a/yarn.lock b/yarn.lock
index b98d3cad..1fd7a3f8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2916,10 +2916,10 @@ globalthis@^1.0.3:
   dependencies:
     define-properties "^1.1.3"
 
-globby@13.1.2:
-  version "13.1.2"
-  resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515"
-  integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==
+globby@13.1.4:
+  version "13.1.4"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317"
+  integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==
   dependencies:
     dir-glob "^3.0.1"
     fast-glob "^3.2.11"
@@ -4320,29 +4320,35 @@ markdown-it@13.0.1:
     mdurl "^1.0.1"
     uc.micro "^1.0.5"
 
-markdownlint-cli2-formatter-default@0.0.3:
-  version "0.0.3"
-  resolved "https://registry.yarnpkg.com/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.3.tgz#5aecd6e576ad18801b76e58bbbaf0e916c583ab8"
-  integrity sha512-QEAJitT5eqX1SNboOD+SO/LNBpu4P4je8JlR02ug2cLQAqmIhh8IJnSK7AcaHBHhNADqdGydnPpQOpsNcEEqCw==
+markdownlint-cli2-formatter-default@0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.4.tgz#81e26b0a50409c0357c6f0d38d8246946b236fab"
+  integrity sha512-xm2rM0E+sWgjpPn1EesPXx5hIyrN2ddUnUwnbCsD/ONxYtw3PX6LydvdH6dciWAoFDpwzbHM1TO7uHfcMd6IYg==
 
-markdownlint-cli2@^0.5.1:
-  version "0.5.1"
-  resolved "https://registry.yarnpkg.com/markdownlint-cli2/-/markdownlint-cli2-0.5.1.tgz#b55b89301f422231a0fc6265794b28cf4da95a82"
-  integrity sha512-f3Nb1GF/c8YSrV/FntsCWzpa5mLFJRlO+wzEgv+lkNQjU6MZflUwc2FbyEDPTo6oVhP2VyUOkK0GkFgfuktl1w==
+markdownlint-cli2@^0.8.1:
+  version "0.8.1"
+  resolved "https://registry.yarnpkg.com/markdownlint-cli2/-/markdownlint-cli2-0.8.1.tgz#84b7c2bb900938d947b1c44bfa940d78656132f8"
+  integrity sha512-y0Siwt+RApKxSSb0CT9p7z1DcAO+ncjrB9IpC/jflJRIet4namCFmxLTbfBBQdPF6EntPk5yyXKe7vcoPGlnXw==
   dependencies:
-    globby "13.1.2"
-    markdownlint "0.26.2"
-    markdownlint-cli2-formatter-default "0.0.3"
+    globby "13.1.4"
+    markdownlint "0.29.0"
+    markdownlint-cli2-formatter-default "0.0.4"
     micromatch "4.0.5"
     strip-json-comments "5.0.0"
-    yaml "2.1.1"
+    yaml "2.3.1"
+
+markdownlint-micromark@0.1.5:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/markdownlint-micromark/-/markdownlint-micromark-0.1.5.tgz#a23400b101be32cd4336f2b6b4c47da31825524c"
+  integrity sha512-HvofNU4QCvfUCWnocQP1IAWaqop5wpWrB0mKB6SSh0fcpV0PdmQNS6tdUuFew1utpYlUvYYzz84oDkrD76GB9A==
 
-markdownlint@0.26.2:
-  version "0.26.2"
-  resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.26.2.tgz#11d3d03e7f0dd3c2e239753ee8fd064a861d9237"
-  integrity sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==
+markdownlint@0.29.0:
+  version "0.29.0"
+  resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.29.0.tgz#9647478b7d5485965c557502fe54ee5a550033f2"
+  integrity sha512-ASAzqpODstu/Qsk0xW5BPgWnK/qjpBQ4e7IpsSvvFXcfYIjanLTdwFRJK1SIEEh0fGSMKXcJf/qhaZYHyME0wA==
   dependencies:
     markdown-it "13.0.1"
+    markdownlint-micromark "0.1.5"
 
 marked-terminal@^5.1.1:
   version "5.1.1"
@@ -5686,10 +5692,10 @@ yallist@^4.0.0:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
-yaml@2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec"
-  integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==
+yaml@2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
+  integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
 
 yargs-parser@^21.0.0, yargs-parser@^21.0.1:
   version "21.0.1"


Test patch:
diff --git a/tests/Query.test.ts b/tests/Query.test.ts
index 6461f7cd..ee1ee269 100644
--- a/tests/Query.test.ts
+++ b/tests/Query.test.ts
@@ -268,6 +268,7 @@ describe('Query parsing', () => {
             'hide recurrence rule',
             'hide scheduled date',
             'hide start date',
+            'hide tags',
             'hide task count',
             'hide urgency',
             'limit 42',
@@ -277,14 +278,15 @@ describe('Query parsing', () => {
             'short mode',
             'short',
             'show backlink',
+            'show created date',
             'show done date',
             'show due date',
             'show edit button',
             'show priority',
             'show recurrence rule',
             'show scheduled date',
-            'show created date',
             'show start date',
+            'show tags',
             'show task count',
             'show urgency',
         ];
diff --git a/tests/TaskLineRenderer.test.ts b/tests/TaskLineRenderer.test.ts
index 4cf398f9..049d9f29 100644
--- a/tests/TaskLineRenderer.test.ts
+++ b/tests/TaskLineRenderer.test.ts
@@ -67,6 +67,7 @@ function getOtherLayoutComponents(parentElement: HTMLElement): string[] {
 describe('task line rendering', () => {
     afterEach(() => {
         resetSettings();
+        GlobalFilter.reset();
     });
 
     it('creates the correct span structure for a basic task', async () => {
@@ -279,6 +280,50 @@ describe('task line rendering', () => {
         );
     });
 
+    it.each([
+        // Nominal cases
+        ['tag at the end #todo', 'tag at the end'],
+        ['tag #tag in the middle', 'tag in the middle'],
+        ['#now tag in the beginning', 'tag in the beginning'],
+        ['#important #s a #today #TODO lot #never of tags #now #PERFORMANCE', 'a lot of tags'],
+        ['nested tags #todo/only/today #x/y/z', 'nested tags'],
+        ['tag with hyphens #a-b-c', 'tag with hyphens'],
+
+        // Border cases
+        ['but      keep extra     spaces', 'but      keep extra     spaces'],
+    ])(
+        'renders without tags task with description "%s"',
+        async (taskDescription: string, renderedDescription: string) => {
+            await testLayoutOptions(
+                `- [ ] ${taskDescription} ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 ➕ 2022-07-05 🔁 every day`,
+                { hideTags: true },
+                renderedDescription,
+                [' ⏫', ' 🔁 every day', ' ➕ 2022-07-05', ' 🛫 2022-07-04', ' ⏳ 2022-07-03', ' 📅 2022-07-02'],
+            );
+        },
+    );
+
+    it('"hide tags" should hide Global Filter when it is a tag despite Remove Global Filter setting', async () => {
+        const globalFilter = '#task';
+        GlobalFilter.set(globalFilter);
+
+        updateSettings({ removeGlobalFilter: false });
+        await testLayoutOptions(
+            `- [ ] ${globalFilter} Global Filter and tags are hidden when Remove Global Filter setting is 'false' #todo`,
+            { hideTags: true },
+            "Global Filter and tags are hidden when Remove Global Filter setting is 'false'",
+            [],
+        );
+
+        updateSettings({ removeGlobalFilter: true });
+        await testLayoutOptions(
+            `- [ ] ${globalFilter} Global Filter and tags are hidden when Remove Global Filter setting is 'true' #todo`,
+            { hideTags: true },
+            "Global Filter and tags are hidden when Remove Global Filter setting is 'true'",
+            [],
+        );
+    });
+
     const testComponentClasses = async (
         taskLine: string,
         layoutOptions: Partial<LayoutOptions>,