Skip to content

Commit 09fcd31

Browse files
authored
Merge pull request #3245 from obsidian-tasks-group/multi-line-placeholders
fix: Allow multi-line properties in {{query.file.property('...')}}
2 parents c684472 + 9c78154 commit 09fcd31

File tree

6 files changed

+370
-124
lines changed

6 files changed

+370
-124
lines changed

resources/sample_vaults/Tasks-Demo/Test Data/query_using_properties.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ task_instruction: group by filename
66
task_instructions: |
77
group by root
88
group by folder
9-
group by filename
9+
group by filename
10+
# a comment
11+
# an indented comment
12+
task_instructions_with_continuation_line: |
13+
path \
14+
includes query_using_properties
15+
task_instruction_with_spaces: " path includes query_using_properties "
1016
---
1117

1218
# query_using_properties
@@ -24,15 +30,41 @@ ignore global query
2430
limit 10
2531
```
2632

33+
## Use a one-line property: task_instruction_with_spaces
34+
35+
Read a Tasks instruction **that is surrounded by extra spaces** from a property in this file, and embed it in to any number of queries in the file:
36+
37+
```tasks
38+
explain
39+
ignore global query
40+
{{query.file.property('task_instruction_with_spaces')}}
41+
limit 10
42+
```
43+
2744
## Use a multi-line property: task_instructions
2845

29-
This fails as the `task_instructions` contains multiple lines , and placeholders are applied after the query is split at line-endings...
46+
Read multiple Tasks instructions from a property in this file, and embed them in to any number of queries in the file:
3047

3148
```tasks
3249
ignore global query
3350
folder includes Test Data
3451
explain
3552
{{query.file.property('task_instructions')}}
53+
limit 10
54+
```
55+
56+
## Use a multi-line property: task_instructions_with_continuation_line
57+
58+
Read multiple Tasks instructions with a continuation line from a property in this file, and embed them in to any number of queries in the file.
59+
60+
Continuation lines are currently unsupported in placeholders.
61+
62+
```tasks
63+
ignore global query
64+
folder includes Test Data
65+
explain
66+
{{query.file.property('task_instructions_with_continuation_line')}}
67+
limit 10
3668
```
3769

3870
## Use a list property in a custom filter: root_dirs_to_search

src/Query/Query.ts

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,24 @@ export class Query implements IQuery {
5757

5858
this.debug(`Creating query: ${this.formatQueryForLogging()}`);
5959

60-
continueLines(source).forEach((statement: Statement) => {
61-
const line = this.expandPlaceholders(statement, tasksFile);
60+
const anyContinuationLinesRemoved = continueLines(source);
61+
62+
const anyPlaceholdersExpanded: Statement[] = [];
63+
for (const statement of anyContinuationLinesRemoved) {
64+
const expandedStatements = this.expandPlaceholders(statement, tasksFile);
6265
if (this.error !== undefined) {
6366
// There was an error expanding placeholders.
6467
return;
6568
}
69+
anyPlaceholdersExpanded.push(...expandedStatements);
70+
}
6671

72+
for (const statement of anyPlaceholdersExpanded) {
6773
try {
68-
this.parseLine(line, statement);
74+
this.parseLine(statement);
75+
if (this.error !== undefined) {
76+
return;
77+
}
6978
} catch (e) {
7079
let message;
7180
if (e instanceof Error) {
@@ -77,7 +86,7 @@ export class Query implements IQuery {
7786
this.setError(message, statement);
7887
return;
7988
}
80-
});
89+
}
8190
}
8291

8392
public get filePath(): string | undefined {
@@ -88,7 +97,8 @@ export class Query implements IQuery {
8897
return this._queryId;
8998
}
9099

91-
private parseLine(line: string, statement: Statement) {
100+
private parseLine(statement: Statement) {
101+
const line = statement.anyPlaceholdersExpanded;
92102
switch (true) {
93103
case this.shortModeRegexp.test(line):
94104
this._queryLayoutOptions.shortMode = true;
@@ -126,20 +136,19 @@ export class Query implements IQuery {
126136
return `[${this.source.split('\n').join(' ; ')}]`;
127137
}
128138

129-
private expandPlaceholders(statement: Statement, tasksFile: OptionalTasksFile) {
139+
private expandPlaceholders(statement: Statement, tasksFile: OptionalTasksFile): Statement[] {
130140
const source = statement.anyContinuationLinesRemoved;
131141
if (source.includes('{{') && source.includes('}}')) {
132142
if (this.tasksFile === undefined) {
133143
this._error = `The query looks like it contains a placeholder, with "{{" and "}}"
134144
but no file path has been supplied, so cannot expand placeholder values.
135145
The query is:
136146
${source}`;
137-
return source;
147+
return [statement];
138148
}
139149
}
140150

141151
// TODO Do not complain about any placeholder errors in comment lines
142-
// TODO Show the original and expanded text in explanations
143152
// TODO Give user error info if they try and put a string in a regex search
144153
let expandedSource: string = source;
145154
if (tasksFile) {
@@ -152,13 +161,36 @@ ${source}`;
152161
} else {
153162
this._error = 'Internal error. expandPlaceholders() threw something other than Error.';
154163
}
155-
return source;
164+
return [statement];
156165
}
157166
}
158167

159-
// Save any expanded text back in to the statement:
160-
statement.recordExpandedPlaceholders(expandedSource);
161-
return expandedSource;
168+
return this.createStatementsFromExpandedPlaceholders(expandedSource, statement);
169+
}
170+
171+
private createStatementsFromExpandedPlaceholders(expandedSource: string, statement: Statement) {
172+
// Trim and filter empty lines in one step.
173+
const expandedSourceLines = expandedSource
174+
.split('\n')
175+
.map((line) => line.trim())
176+
.filter((line) => line.length > 0);
177+
178+
if (expandedSourceLines.length === 1) {
179+
// Save the single expanded line back into the statement.
180+
statement.recordExpandedPlaceholders(expandedSourceLines[0]);
181+
return [statement];
182+
}
183+
184+
// Handle multiple-line placeholders.
185+
return expandedSourceLines.map((expandedSourceLine, index) => {
186+
const counter = `: statement ${index + 1} after expansion of placeholder`;
187+
const newStatement = new Statement(
188+
statement.rawInstruction + counter,
189+
statement.anyContinuationLinesRemoved + counter,
190+
);
191+
newStatement.recordExpandedPlaceholders(expandedSourceLine);
192+
return newStatement;
193+
});
162194
}
163195

164196
/**

src/Renderer/QueryRenderer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ export class QueryRenderer {
4141
// not yet available, so empty.
4242
// - It does not listen out for edits the properties, so if a property is edited,
4343
// the user needs to close and re-open the file.
44-
// - Only single-line properties work. Multiple-line properties give an error message
45-
// 'do not understand query'.
44+
// - Multi-line properties are supported, but they cannot contain
45+
// continuation lines.
4646
const app = this.app;
4747
const filePath = context.sourcePath;
4848
const tFile = app.vault.getAbstractFileByPath(filePath);

0 commit comments

Comments
 (0)