Skip to content

Commit e6d8d89

Browse files
committed
adds support for parsing dates out of headings
1 parent 0c0a816 commit e6d8d89

File tree

5 files changed

+34
-8
lines changed

5 files changed

+34
-8
lines changed

src/parse.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { gfmFromMarkdown } from 'mdast-util-gfm';
1515
import { frontmatterFromMarkdown } from 'mdast-util-frontmatter';
1616

1717
import { extractTagsFromText, extractTagsFromYaml } from './tags.js';
18-
import { FOLDER_META_FILE, joinMergeWhitespace, normalizeWhitespace, SPACE } from './utils.js';
18+
import { DATE_REGEXP, FOLDER_META_FILE, joinMergeWhitespace, normalizeWhitespace, SPACE } from './utils.js';
1919

2020
const WL_REGEXP = /^WL:(\d{1,2}(?:\.\d{1,2})?)[hH]\s/;
2121

@@ -32,6 +32,13 @@ const collectTextDepthFirst = (root: Node | undefined, acc: string = ''): string
3232
return acc;
3333
};
3434

35+
const extractDateFromText = (text: string, tags: TagMap, tag_name: string = 'date') => {
36+
const date_match = text.match(DATE_REGEXP);
37+
if (date_match) {
38+
tags[tag_name] = date_match[1].replaceAll('-', '');
39+
}
40+
};
41+
3542
const parseListItemNode = (node: ListItem, ctx: ParseFileContext, item: Task | Worklog | null) => {
3643
if (!item) {
3744
const text = collectTextDepthFirst(node);
@@ -73,12 +80,13 @@ const parseParentNode = (node: Parent, ctx: ParseFileContext, item: Task | Workl
7380

7481
const parseHeadingNode = (node: Heading, ctx: ParseFileContext, item: Task | Worklog | null) => {
7582
let parent = ctx.heading;
76-
while (parent && parent.depth > node.depth) {
83+
while (parent && parent.depth >= node.depth) {
7784
parent = parent.parent;
7885
}
7986
const tags = parent ? { ...parent.tags } : {};
8087
const text = collectTextDepthFirst(node);
8188
extractTagsFromText(text, tags);
89+
extractDateFromText(text, tags, 'date');
8290
ctx.heading = { depth: node.depth, tags, parent };
8391
};
8492

@@ -126,8 +134,6 @@ const from_markdown_opts = {
126134
mdastExtensions: [frontmatterFromMarkdown(['yaml']), gfmFromMarkdown()],
127135
};
128136

129-
const DATE_IN_FILENAME_REGEXP = /(?:^|[^\d])(\d{8}|(?:\d{4}-\d{2}-\d{2}))(?:$|[^\d])/;
130-
131137
export const parseFile = async (ctx: ParseFileContext) => {
132138
ctx.tasks.forEach((task) => {
133139
if (task.file === ctx.file) {
@@ -142,10 +148,7 @@ export const parseFile = async (ctx: ParseFileContext) => {
142148
try {
143149
const data = await readFile(ctx.file, { encoding: 'utf8' });
144150
const root_node = fromMarkdown(data, from_markdown_opts);
145-
const date_match = ctx.file.match(DATE_IN_FILENAME_REGEXP);
146-
if (date_match) {
147-
ctx.tags['date'] = date_match[1].replaceAll('-', '');
148-
}
151+
extractDateFromText(ctx.file, ctx.tags, 'date');
149152
parseNode(root_node, ctx, null);
150153
} catch (err) {
151154
if ((err as any).code !== 'ENOENT') {

src/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ export const normalizeWhitespace = (text: string) => {
1414
export const joinMergeWhitespace = (a: string, b: string) => {
1515
return `${a.trim()}${SPACE}${b.trim()}`.trim();
1616
};
17+
18+
export const DATE_REGEXP = /(?:^|[^\d])(\d{8}|(?:\d{4}-\d{2}-\d{2}))(?:$|[^\d])/;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
# 2025-01-01
3+
4+
- [ ] a pending task
5+
6+
## 2025-02-02
7+
8+
- [X] a completed task
9+
10+
## heading without date
11+
12+
- [X] another completed task
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"description": "should capture tags from headings",
3+
"argv": ["-t", "text,date"]
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
text | date
2+
---- | ----
3+
a pending task | 20250101
4+
a completed task | 20250202
5+
another completed task | 20250101

0 commit comments

Comments
 (0)