diff --git a/src/Feature.ts b/src/Feature.ts index d5f8d4b538..2096320f6b 100644 --- a/src/Feature.ts +++ b/src/Feature.ts @@ -17,8 +17,19 @@ export class Feature { false, ); + static readonly APPEND_GLOBAL_FILTER = new Feature( + 'APPEND_GLOBAL_FILTER', + 0, + 'Enabling this places the global filter at the end of the task description. Some plugins, such as Day Planner,\n' + + 'might require this, or you might prefer how it looks. If you change this when tasks are modified using the\n' + + 'Task edit box they will have the tag moved to the beginning or end of the description.', + 'Creates / Supports tasks with the global filter at end', + false, + false, + ); + static get values(): Feature[] { - return [this.TASK_STATUS_MENU]; + return [this.TASK_STATUS_MENU, this.TASK_STATUS_MENU]; } static get settingsFlags(): FeatureFlag { diff --git a/src/Task.ts b/src/Task.ts index 8a0ab8a897..910d7a8940 100644 --- a/src/Task.ts +++ b/src/Task.ts @@ -5,8 +5,9 @@ import type { Status } from './Status'; import { replaceTaskWithTasks } from './File'; import { LayoutOptions } from './LayoutOptions'; import { Recurrence } from './Recurrence'; -import { getSettings } from './Settings'; +import { getSettings, isFeatureEnabled } from './Settings'; import { Urgency } from './Urgency'; +import { Feature } from './Feature'; /** * When sorting, make sure low always comes after none. This way any tasks with low will be below any exiting @@ -180,7 +181,14 @@ export class Task { return null; } - let description = body; + // Global filter is applied via edit or to string and no + // longer needs to be on the description. If this happens + // there may be a double space. So all double spaces are made + // single like the UI processing. + let description = body + .replace(globalFilter, '') + .replace(' ', ' ') + .trim(); const indentation = regexMatch[1]; // Get the status of the task. @@ -356,6 +364,8 @@ export class Task { }): Promise { let taskAsString = this.toString(layoutOptions); const { globalFilter, removeGlobalFilter } = getSettings(); + + // Hide the global filter when rendering the query results. if (removeGlobalFilter) { taskAsString = taskAsString.replace(globalFilter, '').trim(); } @@ -439,7 +449,8 @@ export class Task { } /** - * + * Returns a string representation of the task. This is the entire body after the + * markdown task prefix. ( - [ ] ) * * @param {LayoutOptions} [layoutOptions] * @return {*} {string} @@ -447,7 +458,16 @@ export class Task { */ public toString(layoutOptions?: LayoutOptions): string { layoutOptions = layoutOptions ?? new LayoutOptions(); - let taskString = this.description; + + let taskString = this.description.trim(); + const { globalFilter } = getSettings(); + + if (isFeatureEnabled(Feature.APPEND_GLOBAL_FILTER.internalName)) { + taskString = `${taskString} ${globalFilter}`.trim(); + } else { + // Default is to have filter at front. + taskString = `${globalFilter} ${taskString}`.trim(); + } if (!layoutOptions.hidePriority) { let priority: string = ''; @@ -513,7 +533,7 @@ export class Task { public toFileLineString(): string { return `${this.indentation}- [${ this.status.indicator - }] ${this.toString()}`; + }] ${this.toString().trim()}`; } /** diff --git a/src/ui/EditTask.svelte b/src/ui/EditTask.svelte index 9c570b9322..4b981d4864 100644 --- a/src/ui/EditTask.svelte +++ b/src/ui/EditTask.svelte @@ -3,9 +3,10 @@ import { Status } from '../Status'; import { onMount } from 'svelte'; import { Recurrence } from '../Recurrence'; - import { getSettings } from '../Settings'; + import { getSettings, isFeatureEnabled } from '../Settings'; import { Priority, Task } from '../Task'; import { StatusRegistry } from '../StatusRegistry'; + import { Feature } from 'Feature'; export let task: Task; export let onSubmit: (updatedTasks: Task[]) => void | Promise; @@ -158,9 +159,17 @@ const _onSubmit = () => { const { globalFilter } = getSettings(); + let description = editableTask.description.trim(); + + // Check to see if the global filter was added by user. if (!description.includes(globalFilter)) { - description = globalFilter + ' ' + description; + if (isFeatureEnabled(Feature.APPEND_GLOBAL_FILTER.internalName)) { + description = `${description} ${globalFilter}`; + } else { + // Default is to have filter at front. + description = `${globalFilter} ${description}`; + } } let startDate: moment.Moment | null = null; diff --git a/tests/Task.test.ts b/tests/Task.test.ts index 6d59e8d2de..b90b190e1a 100644 --- a/tests/Task.test.ts +++ b/tests/Task.test.ts @@ -4,7 +4,8 @@ import moment from 'moment'; import { Status } from '../src/Status'; import { Task } from '../src/Task'; -import { getSettings, updateSettings } from '../src/Settings'; +import { getSettings, toggleFeature, updateSettings } from '../src/Settings'; +import { Feature } from '../src/Feature'; jest.mock('obsidian'); window.moment = moment; @@ -192,32 +193,28 @@ describe('parsing tags', () => { { markdownTask: '- [x] #someglobaltasktag this is a done task #tagone 🗓 2021-09-12 ✅ 2021-06-20', - expectedDescription: - '#someglobaltasktag this is a done task #tagone', + expectedDescription: 'this is a done task #tagone', extractedTags: ['#tagone'], globalFilter: '#someglobaltasktag', }, { markdownTask: '- [x] #someglobaltasktag this is a done task #tagone #tagtwo 🗓 2021-09-12 ✅ 2021-06-20', - expectedDescription: - '#someglobaltasktag this is a done task #tagone #tagtwo', + expectedDescription: 'this is a done task #tagone #tagtwo', extractedTags: ['#tagone', '#tagtwo'], globalFilter: '#someglobaltasktag', }, { markdownTask: '- [ ] #someglobaltasktag this is a normal task #tagone 🗓 2021-09-12 ✅ 2021-06-20', - expectedDescription: - '#someglobaltasktag this is a normal task #tagone', + expectedDescription: 'this is a normal task #tagone', extractedTags: ['#tagone'], globalFilter: '#someglobaltasktag', }, { markdownTask: '- [ ] #someglobaltasktag this is a normal task #tagone #tagtwo 🗓 2021-09-12 ✅ 2021-06-20', - expectedDescription: - '#someglobaltasktag this is a normal task #tagone #tagtwo', + expectedDescription: 'this is a normal task #tagone #tagtwo', extractedTags: ['#tagone', '#tagtwo'], globalFilter: '#someglobaltasktag', }, @@ -225,7 +222,7 @@ describe('parsing tags', () => { markdownTask: '- [ ] #someglobaltasktag this is a normal task #tagone #tag/with/depth #tagtwo 🗓 2021-09-12 ✅ 2021-06-20', expectedDescription: - '#someglobaltasktag this is a normal task #tagone #tag/with/depth #tagtwo', + 'this is a normal task #tagone #tag/with/depth #tagtwo', extractedTags: ['#tagone', '#tag/with/depth', '#tagtwo'], globalFilter: '#someglobaltasktag', }, @@ -275,6 +272,13 @@ describe('parsing tags', () => { ); }); +type GlobalFilterParsingExpectations = { + location: string; + scenario: string; + initialTask: string; + expectedTask: string; +}; + describe('to string', () => { it('retains the block link', () => { // Arrange @@ -311,6 +315,77 @@ describe('to string', () => { // Assert expect(task.toFileLineString()).toStrictEqual(line); }); + + test.each([ + { + location: 'append', + scenario: 'at front', + initialTask: + '- [x] #globalfilter this is a done task #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + expectedTask: + '- [x] this is a done task #tagone #journal/daily #globalfilter 📅 2021-09-12 ✅ 2021-06-20', + }, + { + location: 'append', + scenario: 'at end', + initialTask: + '- [x] this is a done task #tagone #journal/daily #globalfilter 📅 2021-09-12 ✅ 2021-06-20', + expectedTask: + '- [x] this is a done task #tagone #journal/daily #globalfilter 📅 2021-09-12 ✅ 2021-06-20', + }, + { + location: 'append', + scenario: 'in middle', + initialTask: + '- [x] this is a done task #globalfilter #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + expectedTask: + '- [x] this is a done task #tagone #journal/daily #globalfilter 📅 2021-09-12 ✅ 2021-06-20', + }, + { + location: 'prepend', + scenario: 'at front', + initialTask: + '- [x] #globalfilter this is a done task #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + expectedTask: + '- [x] #globalfilter this is a done task #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + }, + { + location: 'prepend', + scenario: 'at end', + initialTask: + '- [x] this is a done task #tagone #journal/daily #globalfilter 📅 2021-09-12 ✅ 2021-06-20', + expectedTask: + '- [x] #globalfilter this is a done task #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + }, + { + location: 'prepend', + scenario: 'in middle', + initialTask: + '- [x] this is a done task #globalfilter #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + expectedTask: + '- [x] #globalfilter this is a done task #tagone #journal/daily 📅 2021-09-12 ✅ 2021-06-20', + }, + ])( + 'should $location global filter when $scenario', + ({ location, initialTask, expectedTask }) => { + // Arrange + const originalSettings = getSettings(); + updateSettings({ globalFilter: '#globalfilter' }); + if (location === 'append') { + toggleFeature(Feature.APPEND_GLOBAL_FILTER.internalName, true); + } + // Act + const task = constructTaskFromLine(initialTask); + + // Assert + expect(task).not.toBeNull(); + expect(task?.toFileLineString()).toStrictEqual(expectedTask); + + // Cleanup + updateSettings(originalSettings); + toggleFeature(Feature.APPEND_GLOBAL_FILTER.internalName, false); + }, + ); }); describe('toggle done', () => {