Skip to content

Commit

Permalink
Query dates can now be relative
Browse files Browse the repository at this point in the history
See the README for details.

Fixes #1
  • Loading branch information
schemar committed May 5, 2021
1 parent 8b89382 commit 1a12c86
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 15 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ The simplest way to query tasks is this:

In preview mode, this will list *all* tasks from your vault, regardless of their properties like status.
This is probably not what you want. Therefore, Tasks allows you to filter the tasks that you want to show.
All date filters must be in the format `YYYY-MM-DD`, meaning `Year-Month-Day` with leading zeros.

The following filters exist:

Expand All @@ -193,6 +192,20 @@ The following filters exist:
- Only lists the first `<number>` tasks of the result.
- Shorthand is `limit <number>`.

#### Dates
`<date>` filters can be given in natural language or in formal notation.
The following are some examples of valid `<date>` filters as inspiration:
- `2021-05-05`
- `today`
- `tomorrow`
- `next monday`
- `last friday`
- `in two weeks`

Note that if it is Wednesday and you write `tuesday`, Tasks assumes you mean "yesterday", as that is the closest Tuesday.
Use `next tuesday` instead if you mean "next tuesday".

#### Matching
All filters of a query have to match in order for a task to be listed.
This means you cannot show tasks that have "GitHub in the path and have no due date or are due after 2021-04-04".
Instead you would have two queries, one for each condition:
Expand All @@ -213,6 +226,21 @@ Instead you would have two queries, one for each condition:

#### Examples

All open tasks that are due today:

```tasks
not done
due today
```

All open tasks that are due within the next two weeks, but are not overdue (due today or later):

```tasks
not done
due after yesterday
due before in two weeks
```

Show all tasks that aren't done, are due on the 9th of April 2021, and where the path includes `GitHub`:

```tasks
Expand Down
45 changes: 32 additions & 13 deletions src/Query.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import chrono from 'chrono-node';
import moment from 'moment';

import { Status, Task } from './Task';

export class Query {
private _limit: number | undefined = undefined;
private _filters: ((task: Task) => boolean)[] = [];
private _error: string | undefined = undefined;

private readonly noDueString = 'no due date';
private readonly dueRegexp = /due (before|after|on) (\d{4}-\d{2}-\d{2})/;
private readonly dueRegexp = /due (before|after|on)? ?(.*)/;
private readonly doneString = 'done';
private readonly notDoneString = 'not done';
private readonly doneRegexp = /done (before|after|on) (\d{4}-\d{2}-\d{2})/;
private readonly doneRegexp = /done (before|after|on)? ?(.*)/;
private readonly pathRegexp = /path (includes|does not include) (.*)/;
private readonly descriptionRegexp = /description (includes|does not include) (.*)/;
private readonly limitRegexp = /limit (to )?(\d+)( tasks?)?/;
Expand Down Expand Up @@ -54,6 +57,8 @@ export class Query {
case this.limitRegexp.test(line):
this.parseLimit({ line });
break;
default:
this._error = 'do not understand query';
}
});
}
Expand All @@ -66,11 +71,19 @@ export class Query {
return this._filters;
}

public get error(): string | undefined {
return this._error;
}

private parseDueFilter({ line }: { line: string }): void {
const dueMatch = line.match(this.dueRegexp);
if (dueMatch !== null) {
const filterDate = this.parseDate(dueMatch[2]);
if (!filterDate.isValid()) {
this._error = 'do not understand due date';
}

let filter;
const filterDate = moment(dueMatch[2], Task.dateFormat);
if (dueMatch[1] === 'before') {
filter = (task: Task) =>
task.dueDate ? task.dueDate.isBefore(filterDate) : false;
Expand All @@ -84,15 +97,19 @@ export class Query {

this._filters.push(filter);
} else {
console.error('Tasks: unknown query filter (due date):', line);
this._error = 'do not understand query filter (due date)';
}
}

private parseDoneFilter({ line }: { line: string }): void {
const doneMatch = line.match(this.doneRegexp);
if (doneMatch !== null) {
const filterDate = this.parseDate(doneMatch[2]);
if (!filterDate.isValid()) {
this._error = 'do not understand done date';
}

let filter;
const filterDate = moment(doneMatch[2], Task.dateFormat);
if (doneMatch[1] === 'before') {
filter = (task: Task) =>
task.doneDate ? task.doneDate.isBefore(filterDate) : false;
Expand Down Expand Up @@ -121,10 +138,10 @@ export class Query {
(task: Task) => !task.path.includes(pathMatch[2]),
);
} else {
console.error('Tasks: unknown query filter (path):', line);
this._error = 'do not understand query filter (path)';
}
} else {
console.error('Tasks: unknown query filter (path):', line);
this._error = 'do not understand query filter (path)';
}
}

Expand All @@ -142,13 +159,10 @@ export class Query {
!task.description.includes(descriptionMatch[2]),
);
} else {
console.error(
'Tasks: unknown query filter (description):',
line,
);
this._error = 'do not understand query filter (description)';
}
} else {
console.error('Tasks: unknown query filter (description):', line);
this._error = 'do not understand query filter (description)';
}
}

Expand All @@ -159,7 +173,12 @@ export class Query {
const limit = Number.parseInt(limitMatch[2], 10);
this._limit = limit;
} else {
console.error('Tasks: unknown query limit:', line);
this._error = 'do not understand query limit';
}
}

private parseDate(input: string): moment.Moment {
// Using start of date to correctly match on comparison with other dates (like equality).
return moment(chrono.parseDate(input)).startOf('day');
}
}
7 changes: 6 additions & 1 deletion src/QueryRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ class QueryRenderChild extends MarkdownRenderChild {

private async render() {
const content = this.containerEl.createEl('div');
if (this.cache.getState() === State.Warm) {
if (
this.cache.getState() === State.Warm &&
this.query.error === undefined
) {
const { taskList, tasksCount } = await this.createTasksList(
content,
);
Expand All @@ -80,6 +83,8 @@ class QueryRenderChild extends MarkdownRenderChild {
text: `${tasksCount} task${tasksCount !== 1 ? 's' : ''}`,
cls: 'tasks-count',
});
} else if (this.query.error !== undefined) {
content.innerHTML = `Tasks query: ${this.query.error}`;
} else {
content.innerHTML = 'Loading Tasks ...';
}
Expand Down

0 comments on commit 1a12c86

Please sign in to comment.