Skip to content

Commit 7a4ccaf

Browse files
authored
Merge pull request #3402 from obsidian-tasks-group/fix-numbered-list-with-paren
fix: Support Check boxes and List Items in "1)"-style numbered lists Fixes #3401
2 parents ea3207e + 90d5940 commit 7a4ccaf

File tree

9 files changed

+463
-7
lines changed

9 files changed

+463
-7
lines changed

docs/Getting Started/Getting Started.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@ Now Tasks tracks that you need to take out the trash!
2626
>
2727
> + [ ] task starting with a plus sign
2828
>
29-
> 1. [ ] a task in a numbered list
29+
> 1. [ ] a task in a numbered list with a `.` character
30+
>
31+
> 2) [ ] a task in a numbered list with a `)` character
3032
> ```
3133
3234
> [!released]
33-
> Support for tasks with `+` was introduced in Tasks 4.5.0.
35+
>
36+
> - Support for tasks with `+` was introduced in Tasks 4.5.0.
37+
> - Support for numbered lists with `)` was introduced in Tasks X.Y.Z.
3438
3539
To list all open tasks in a markdown file, simply add a [[About Queries|query]] as a tasks code block like so:
3640
@@ -118,7 +122,9 @@ We are tracking this in [issue #2061](https://github.com/obsidian-tasks-group/ob
118122
Tasks can read tasks that are in **numbered lists**.
119123

120124
> [!released]
121-
Reading tasks inside numbered lists was introduced in Tasks 1.20.0.
125+
>
126+
> - Reading tasks inside numbered lists was introduced in Tasks 1.20.0.
127+
> - Reading tasks inside numbered lists with `)` was introduced in Tasks X.Y.Z.
122128
123129
For example:
124130

@@ -128,12 +134,20 @@ For example:
128134
3. [ ] Do following step
129135
```
130136

137+
or:
138+
139+
```markdown
140+
1) [ ] Do first step
141+
2) [ ] Do next step
142+
3) [ ] Do following step
143+
```
144+
131145
Editing and toggling tasks in numbered lists works fine: the original number is preserved.
132146

133147
> [!warning]
134148
> However, when these tasks are displayed in tasks blocks they are displayed as ordinary bullet list items.
135-
136-
This is because they will usually be displayed in a completely different order than in the original list, often mixed in with tasks from bullet lists. The original numbers in this case just don't make sense.
149+
>
150+
> This is because they will usually be displayed in a completely different order than in the original list, often mixed in with tasks from bullet lists. The original numbers in this case just don't make sense.
137151
138152
### Tasks in Blockquotes and Callouts
139153

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# numbered_list_items_standard
2+
3+
1. [ ] #task Task 1 in 'numbered_list_items_standard'
4+
1. Sub-item 1
5+
2. [ ] #task Task 2 in 'numbered_list_items_standard'
6+
1. Sub-item 2
7+
3. List item in 'numbered_list_items_standard'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# numbered_list_items_with_paren
2+
3+
1) [ ] #task Task 1 in 'numbered_list_items_with_paren'
4+
1) Sub-item 1
5+
2) [ ] #task Task 2 in 'numbered_list_items_with_paren'
6+
1) Sub-item 2
7+
3) List item in 'numbered_list_items_with_paren'

src/Task/TaskRegularExpressions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export class TaskRegularExpressions {
55
// Matches indentation before a list marker (including > for potentially nested blockquotes or Obsidian callouts)
66
public static readonly indentationRegex = /^([\s\t>]*)/;
77

8-
// Matches - * and + list markers, or numbered list markers (eg 1.)
9-
public static readonly listMarkerRegex = /([-*+]|[0-9]+\.)/;
8+
// Matches - * and + list markers, or numbered list markers, for example 1. and 1)
9+
public static readonly listMarkerRegex = /([-*+]|[0-9]+[.)])/;
1010

1111
// Matches a checkbox and saves the status character inside
1212
public static readonly checkboxRegex = /\[(.)\]/u;

tests/Obsidian/AllCacheSampleData.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ import multiple_headings from './__test_data__/multiple_headings.json';
5353
import no_heading from './__test_data__/no_heading.json';
5454
import no_yaml from './__test_data__/no_yaml.json';
5555
import non_tasks from './__test_data__/non_tasks.json';
56+
import numbered_list_items_standard from './__test_data__/numbered_list_items_standard.json';
57+
import numbered_list_items_with_paren from './__test_data__/numbered_list_items_with_paren.json';
5658
import one_task from './__test_data__/one_task.json';
5759
import query_file_defaults_all_options_false from './__test_data__/query_file_defaults_all_options_false.json';
5860
import query_file_defaults_all_options_null from './__test_data__/query_file_defaults_all_options_null.json';
@@ -132,6 +134,8 @@ export function allCacheSampleData() {
132134
no_heading,
133135
no_yaml,
134136
non_tasks,
137+
numbered_list_items_standard,
138+
numbered_list_items_with_paren,
135139
one_task,
136140
query_file_defaults_all_options_false,
137141
query_file_defaults_all_options_null,

tests/Obsidian/Cache.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import inheritance_task_listitem from './__test_data__/inheritance_task_listitem
2626
import inheritance_task_listitem_mixed_grandchildren from './__test_data__/inheritance_task_listitem_mixed_grandchildren.json';
2727
import inheritance_task_listitem_task from './__test_data__/inheritance_task_listitem_task.json';
2828
import inheritance_task_mixed_children from './__test_data__/inheritance_task_mixed_children.json';
29+
import numbered_list_items_with_paren from './__test_data__/numbered_list_items_with_paren.json';
30+
import numbered_list_items_standard from './__test_data__/numbered_list_items_standard.json';
2931
import one_task from './__test_data__/one_task.json';
3032
import callouts_nested_issue_2890_labelled from './__test_data__/callouts_nested_issue_2890_labelled.json';
3133
import callout from './__test_data__/callout.json';
@@ -124,6 +126,57 @@ describe('cache', () => {
124126
testRootAndChildren(sibling2, []);
125127
});
126128

129+
it('should read numbered list items with dot', () => {
130+
const data = numbered_list_items_standard;
131+
const tasks = readTasksFromSimulatedFile(data);
132+
expect(data.fileContents).toMatchInlineSnapshot(`
133+
"# numbered_list_items_standard
134+
135+
1. [ ] #task Task 1 in 'numbered_list_items_standard'
136+
1. Sub-item 1
137+
2. [ ] #task Task 2 in 'numbered_list_items_standard'
138+
1. Sub-item 2
139+
3. List item in 'numbered_list_items_standard'
140+
"
141+
`);
142+
expect(tasks[0].file.cachedMetadata.listItems?.length).toEqual(5);
143+
144+
expect(printRoots(tasks)).toMatchInlineSnapshot(`
145+
"1. [ ] #task Task 1 in 'numbered_list_items_standard' : Task
146+
1. Sub-item 1 : ListItem
147+
2. [ ] #task Task 2 in 'numbered_list_items_standard' : Task
148+
1. Sub-item 2 : ListItem
149+
"
150+
`);
151+
expect(tasks.length).toEqual(2);
152+
});
153+
154+
it('should read numbered list items with closing parenthesis', () => {
155+
// See https://github.com/obsidian-tasks-group/obsidian-tasks/issues/3401
156+
// "Unexpected failure to create a list item from line" warning when parsing "1)" style numbered list
157+
const data = numbered_list_items_with_paren;
158+
const tasks = readTasksFromSimulatedFile(data);
159+
expect(data.fileContents).toMatchInlineSnapshot(`
160+
"# numbered_list_items_with_paren
161+
162+
1) [ ] #task Task 1 in 'numbered_list_items_with_paren'
163+
1) Sub-item 1
164+
2) [ ] #task Task 2 in 'numbered_list_items_with_paren'
165+
1) Sub-item 2
166+
3) List item in 'numbered_list_items_with_paren'
167+
"
168+
`);
169+
170+
expect(printRoots(tasks)).toMatchInlineSnapshot(`
171+
"1) [ ] #task Task 1 in 'numbered_list_items_with_paren' : Task
172+
1) Sub-item 1 : ListItem
173+
2) [ ] #task Task 2 in 'numbered_list_items_with_paren' : Task
174+
1) Sub-item 2 : ListItem
175+
"
176+
`);
177+
expect(tasks.length).toEqual(2);
178+
});
179+
127180
it('should read one parent and one child task', () => {
128181
const tasks = readTasksFromSimulatedFile(inheritance_1parent1child);
129182
expect(inheritance_1parent1child.fileContents).toMatchInlineSnapshot(`
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
{
2+
"cachedMetadata": {
3+
"headings": [
4+
{
5+
"heading": "numbered_list_items_standard",
6+
"level": 1,
7+
"position": {
8+
"end": {
9+
"col": 30,
10+
"line": 0,
11+
"offset": 30
12+
},
13+
"start": {
14+
"col": 0,
15+
"line": 0,
16+
"offset": 0
17+
}
18+
}
19+
}
20+
],
21+
"listItems": [
22+
{
23+
"parent": -2,
24+
"position": {
25+
"end": {
26+
"col": 53,
27+
"line": 2,
28+
"offset": 85
29+
},
30+
"start": {
31+
"col": 0,
32+
"line": 2,
33+
"offset": 32
34+
}
35+
},
36+
"task": " "
37+
},
38+
{
39+
"parent": 2,
40+
"position": {
41+
"end": {
42+
"col": 17,
43+
"line": 3,
44+
"offset": 103
45+
},
46+
"start": {
47+
"col": 4,
48+
"line": 3,
49+
"offset": 90
50+
}
51+
}
52+
},
53+
{
54+
"parent": -2,
55+
"position": {
56+
"end": {
57+
"col": 53,
58+
"line": 4,
59+
"offset": 157
60+
},
61+
"start": {
62+
"col": 0,
63+
"line": 4,
64+
"offset": 104
65+
}
66+
},
67+
"task": " "
68+
},
69+
{
70+
"parent": 4,
71+
"position": {
72+
"end": {
73+
"col": 17,
74+
"line": 5,
75+
"offset": 175
76+
},
77+
"start": {
78+
"col": 4,
79+
"line": 5,
80+
"offset": 162
81+
}
82+
}
83+
},
84+
{
85+
"parent": -2,
86+
"position": {
87+
"end": {
88+
"col": 46,
89+
"line": 6,
90+
"offset": 222
91+
},
92+
"start": {
93+
"col": 0,
94+
"line": 6,
95+
"offset": 176
96+
}
97+
}
98+
}
99+
],
100+
"sections": [
101+
{
102+
"position": {
103+
"end": {
104+
"col": 30,
105+
"line": 0,
106+
"offset": 30
107+
},
108+
"start": {
109+
"col": 0,
110+
"line": 0,
111+
"offset": 0
112+
}
113+
},
114+
"type": "heading"
115+
},
116+
{
117+
"position": {
118+
"end": {
119+
"col": 46,
120+
"line": 6,
121+
"offset": 222
122+
},
123+
"start": {
124+
"col": 0,
125+
"line": 2,
126+
"offset": 32
127+
}
128+
},
129+
"type": "list"
130+
}
131+
],
132+
"tags": [
133+
{
134+
"position": {
135+
"end": {
136+
"col": 12,
137+
"line": 2,
138+
"offset": 44
139+
},
140+
"start": {
141+
"col": 7,
142+
"line": 2,
143+
"offset": 39
144+
}
145+
},
146+
"tag": "#task"
147+
},
148+
{
149+
"position": {
150+
"end": {
151+
"col": 12,
152+
"line": 4,
153+
"offset": 116
154+
},
155+
"start": {
156+
"col": 7,
157+
"line": 4,
158+
"offset": 111
159+
}
160+
},
161+
"tag": "#task"
162+
}
163+
]
164+
},
165+
"fileContents": "# numbered_list_items_standard\n\n1. [ ] #task Task 1 in 'numbered_list_items_standard'\n 1. Sub-item 1\n2. [ ] #task Task 2 in 'numbered_list_items_standard'\n 1. Sub-item 2\n3. List item in 'numbered_list_items_standard'\n",
166+
"filePath": "Test Data/numbered_list_items_standard.md",
167+
"getAllTags": [
168+
"#task",
169+
"#task"
170+
],
171+
"parseFrontMatterTags": null
172+
}

0 commit comments

Comments
 (0)