Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2385 (2023-10-30 20:57:05)

feat: Statuses report now shows a Status Settings table
<!--- Provide a general summary of your changes in the Title above -->

# Description

The `Review and check your Statuses` button now shows a table of the statuses from the user's Tasks Status settings.

_Note: I'm planning on changing this some more, so am holding off writing the documentation for now._

## Motivation and Context

Builds on https://github.com/obsidian-tasks-group/obsidian-tasks/pull/2383 - showing more information.

A later step may add some error messages/feedback to the table.

## How has this been tested?

- By updating the test for the table output
- By repeated generating of the report in the Tasks-Demo vault.

## Screenshots (if appropriate)

![image](https://github.com/obsidian-tasks-group/obsidian-tasks/assets/4840096/a0e4c63a-c7f9-40e0-919a-dd9a2689f8d3)


## 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)

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.
- [ ] 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/Config/StatusSettings.ts b/src/Config/StatusSettings.ts
index 2ffe29ee..a66c4ee4 100644
--- a/src/Config/StatusSettings.ts
+++ b/src/Config/StatusSettings.ts
@@ -163,6 +163,14 @@ export class StatusSettings {
         return notices;
     }
 
+    /**
+     * Retun a list of all the statuses in the settings - first the core ones, then the custom ones.
+     * @param statusSettings
+     */
+    public static allStatuses(statusSettings: StatusSettings) {
+        return statusSettings.coreStatuses.concat(statusSettings.customStatuses);
+    }
+
     /**
      * Apply the custom statuses in the statusSettings object to the statusRegistry.
      * @param statusSettings
@@ -170,10 +178,7 @@ export class StatusSettings {
      */
     public static applyToStatusRegistry(statusSettings: StatusSettings, statusRegistry: StatusRegistry) {
         statusRegistry.clearStatuses();
-        statusSettings.coreStatuses.forEach((statusType) => {
-            statusRegistry.add(statusType);
-        });
-        statusSettings.customStatuses.forEach((statusType) => {
+        StatusSettings.allStatuses(statusSettings).forEach((statusType) => {
             statusRegistry.add(statusType);
         });
     }
diff --git a/src/StatusRegistryReport.ts b/src/StatusRegistryReport.ts
index 8a7cd5e5..99e60d37 100644
--- a/src/StatusRegistryReport.ts
+++ b/src/StatusRegistryReport.ts
@@ -1,8 +1,28 @@
 import type { StatusRegistry } from './StatusRegistry';
-import type { StatusSettings } from './Config/StatusSettings';
+import { StatusSettings } from './Config/StatusSettings';
+import { MarkdownTable } from './lib/MarkdownTable';
+import type { StatusConfiguration } from './StatusConfiguration';
+
+export function tabulateStatusSettings(statusSettings: StatusSettings) {
+    // Note: There is very similar code in verifyStatusesAsMarkdownTable() in DocsSamplesForStatuses.test.ts.
+    //       Maybe try unifying the common code one day?
+
+    const table = new MarkdownTable(['Status Symbol', 'Next Status Symbol', 'Status Name', 'Status Type']);
+
+    const statuses: StatusConfiguration[] = StatusSettings.allStatuses(statusSettings);
+    for (const status of statuses) {
+        table.addRow([
+            getPrintableSymbol(status.symbol),
+            getPrintableSymbol(status.nextStatusSymbol),
+            status.name,
+            getPrintableSymbol(status.type),
+        ]);
+    }
+    return table.markdown;
+}
 
 export function createStatusRegistryReport(
-    _statusSettings: StatusSettings,
+    statusSettings: StatusSettings,
     statusRegistry: StatusRegistry,
     buttonName: string,
     versionString: string,
@@ -13,13 +33,41 @@ export function createStatusRegistryReport(
     // - Show any status transitions that won't work with recurring tasks currently, as DONE not followed by TODO.
 
     const detailed = true;
+    const settingsTable = tabulateStatusSettings(statusSettings);
     const mermaidText = statusRegistry.mermaidDiagram(detailed);
     return `# ${buttonName}
 
+## About this file
+
 This file was created by the Obsidian Tasks plugin (version ${versionString}) to help visualise the task statuses in this vault.
 
+If you change the Tasks status settings, you can get an updated report by:
+
+- Going to \`Settings\` -> \`Tasks\`.
+- Clicking on \`Review and check your Statuses\`.
+
 You can delete this file any time.
 
+## Status Settings
+
+<!--
+Switch to Live Preview or Reading Mode to see the table.
+If there are any Markdown formatting characters in status names, such as '*' or '_',
+Obsidian may only render the table correctly in Reading Mode.
+-->
+
+These are the status values in the Core and Custom statuses sections.
+
+${settingsTable}
+## Loaded Settings
+
 <!-- Switch to Live Preview or Reading Mode to see the diagram. -->
+
+These are the settings actually used by Tasks.
 ${mermaidText}`;
 }
+
+export function getPrintableSymbol(symbol: string) {
+    const result = symbol !== ' ' ? symbol : 'space';
+    return '`' + result + '`';
+}


Test patch:
diff --git a/tests/Config/StatusSettings.test.ts b/tests/Config/StatusSettings.test.ts
index 9b97f4d8..f5ee2949 100644
--- a/tests/Config/StatusSettings.test.ts
+++ b/tests/Config/StatusSettings.test.ts
@@ -159,6 +159,18 @@ describe('StatusSettings', () => {
         `);
     });
 
+    it('should return a combined list of all statuses', () => {
+        // Arrange
+        const settings = new StatusSettings();
+
+        // Act
+        const allStatuses = StatusSettings.allStatuses(settings);
+
+        // Assert
+        const allSymbolsInOrder = allStatuses.map((status) => status.symbol).join('|');
+        expect(allSymbolsInOrder).toEqual(' |x|/|-');
+    });
+
     it('should apply settings to a StatusRegistry', () => {
         // Arrange
         const settings = new StatusSettings();
diff --git a/tests/DocumentationSamples/DocsSamplesForStatuses.test.ts b/tests/DocumentationSamples/DocsSamplesForStatuses.test.ts
index cf19ce07..45085af5 100644
--- a/tests/DocumentationSamples/DocsSamplesForStatuses.test.ts
+++ b/tests/DocumentationSamples/DocsSamplesForStatuses.test.ts
@@ -18,13 +18,11 @@ import { verifyWithFileExtension } from '../TestingTools/ApprovalTestHelpers';
 import { SearchInfo } from '../../src/Query/SearchInfo';
 import type { GrouperFunction } from '../../src/Query/Grouper';
 import { MarkdownTable } from '../../src/lib/MarkdownTable';
-
-function getPrintableSymbol(symbol: string) {
-    const result = symbol !== ' ' ? symbol : 'space';
-    return '`' + result + '`';
-}
+import { getPrintableSymbol } from '../../src/StatusRegistryReport';
 
 function verifyStatusesAsMarkdownTable(statuses: Status[], showQueryInstructions: boolean) {
+    // Note: There is very similar code in tabulateStatusSettings() in StatusRegistryReport.ts.
+    //       Maybe try unifying the common code one day?
     let statusName = 'Status Name';
     let statusType = 'Status Type';
     if (showQueryInstructions) {
diff --git a/tests/StatusRegistryReport.test.StatusRegistryReport_should_create_a_report.approved.md b/tests/StatusRegistryReport.test.StatusRegistryReport_should_create_a_report.approved.md
index 6840e706..e7b54940 100644
--- a/tests/StatusRegistryReport.test.StatusRegistryReport_should_create_a_report.approved.md
+++ b/tests/StatusRegistryReport.test.StatusRegistryReport_should_create_a_report.approved.md
@@ -1,11 +1,42 @@
 # Review and check your Statuses
 
+## About this file
+
 This file was created by the Obsidian Tasks plugin (version x.y.z) to help visualise the task statuses in this vault.
 
+If you change the Tasks status settings, you can get an updated report by:
+
+- Going to `Settings` -> `Tasks`.
+- Clicking on `Review and check your Statuses`.
+
 You can delete this file any time.
 
+## Status Settings
+
+<!--
+Switch to Live Preview or Reading Mode to see the table.
+If there are any Markdown formatting characters in status names, such as '*' or '_',
+Obsidian may only render the table correctly in Reading Mode.
+-->
+
+These are the status values in the Core and Custom statuses sections.
+
+| Status Symbol | Next Status Symbol | Status Name | Status Type |
+| ----- | ----- | ----- | ----- |
+| `space` | `x` | Todo | `TODO` |
+| `x` | `space` | Done | `DONE` |
+| `/` | `x` | In Progress | `IN_PROGRESS` |
+| `-` | `space` | Cancelled | `CANCELLED` |
+| `Q` | `A` | Question | `NON_TASK` |
+| `A` | `Q` | Answer | `NON_TASK` |
+| `` | `` |  | `TODO` |
+
+## Loaded Settings
+
 <!-- Switch to Live Preview or Reading Mode to see the diagram. -->
 
+These are the settings actually used by Tasks.
+
 ```mermaid
 flowchart LR
 1["'Todo'<br>[ ] -> [x]<br>(TODO)"]
diff --git a/tests/StatusRegistryReport.test.StatusRegistryReport_should_tabulate_StatusSettings.approved.md b/tests/StatusRegistryReport.test.StatusRegistryReport_should_tabulate_StatusSettings.approved.md
new file mode 100644
index 00000000..767ed174
--- /dev/null
+++ b/tests/StatusRegistryReport.test.StatusRegistryReport_should_tabulate_StatusSettings.approved.md
@@ -0,0 +1,6 @@
+| Status Symbol | Next Status Symbol | Status Name | Status Type |
+| ----- | ----- | ----- | ----- |
+| `space` | `x` | Todo | `TODO` |
+| `x` | `space` | Done | `DONE` |
+| `/` | `x` | In Progress | `IN_PROGRESS` |
+| `-` | `space` | Cancelled | `CANCELLED` |
diff --git a/tests/StatusRegistryReport.test.ts b/tests/StatusRegistryReport.test.ts
index dde24d46..94a3aae7 100644
--- a/tests/StatusRegistryReport.test.ts
+++ b/tests/StatusRegistryReport.test.ts
@@ -1,4 +1,4 @@
-import { createStatusRegistryReport } from '../src/StatusRegistryReport';
+import { createStatusRegistryReport, tabulateStatusSettings } from '../src/StatusRegistryReport';
 import { StatusRegistry } from '../src/StatusRegistry';
 import { StatusSettings } from '../src/Config/StatusSettings';
 import type { StatusCollection, StatusCollectionEntry } from '../src/StatusCollection';
@@ -16,6 +16,7 @@ function createStatuses(
     StatusSettings.replaceStatus(core, core[0], Status.createFromImportedValue(coreStatusesData[0]));
     StatusSettings.replaceStatus(core, core[1], Status.createFromImportedValue(coreStatusesData[1]));
 
+    StatusSettings.deleteAllCustomStatuses(statusSettings);
     customStatusesData.map((entry: StatusCollectionEntry) => {
         StatusSettings.addStatus(statusSettings.customStatuses, Status.createFromImportedValue(entry));
     });
@@ -27,6 +28,12 @@ function createStatuses(
 }
 
 describe('StatusRegistryReport', function () {
+    it('should tabulate StatusSettings', () => {
+        const statusSettings = new StatusSettings();
+        const markdown = tabulateStatusSettings(statusSettings);
+        verifyWithFileExtension(markdown, '.md');
+    });
+
     it('should create a report', () => {
         // Arrange
 
@@ -36,8 +43,11 @@ describe('StatusRegistryReport', function () {
         ];
 
         const customStatusesData: StatusCollection = [
+            ['/', 'In Progress', 'x', 'IN_PROGRESS'],
+            ['-', 'Cancelled', ' ', 'CANCELLED'],
             ['Q', 'Question', 'A', 'NON_TASK'],
             ['A', 'Answer', 'Q', 'NON_TASK'],
+            ['', '', '', 'TODO'], // A new, unedited status
         ];
         const { statusSettings, statusRegistry } = createStatuses(coreStatusesData, customStatusesData);