Skip to content

Commit 7e2bb49

Browse files
authored
Merge pull request #3485 from obsidian-tasks-group/feat-presets
feat!!: Rename 'include' facility to 'preset'
2 parents 9288365 + 8ce8198 commit 7e2bb49

File tree

16 files changed

+388
-138
lines changed

16 files changed

+388
-138
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
publish: true
3+
---
4+
5+
# ADR-001 - Nomenclature for Reusable Instruction Blocks Feature
6+
7+
## Status
8+
9+
**ACCEPTED** - Decision made on 2025-05-29
10+
11+
## Context
12+
13+
The Tasks plugin introduced a feature allowing users to define reusable blocks of Tasks instructions in settings and reference them in queries. This feature was initially named "Includes" with the following syntax:
14+
15+
- Direct instruction: `include standard_options`
16+
- Placeholder syntax: `{{includes.standard_options}}`
17+
18+
However, during pre-release testing, a significant naming collision was identified with the existing text search functionality:
19+
20+
- Existing: `description includes blah` (searches for text containing "blah")
21+
- New feature: `include standard_options` (imports predefined instruction block)
22+
23+
This collision creates cognitive overhead and potential user confusion, necessitating a nomenclature change before public release.
24+
25+
## Decision
26+
27+
We will rename the feature from "Includes" to "Presets" with the following syntax:
28+
29+
- **Direct instruction:** `preset standard_options`
30+
- **Placeholder syntax:** `{{preset.standard_options}}` (singular for consistency)
31+
32+
## Alternatives Considered
33+
34+
The following alternatives were evaluated in priority order:
35+
36+
| Priority | Direct Syntax | Placeholder Syntax | Assessment |
37+
| -------- | ----------------------------- | ---------------------------------- | ----------------------------------------------- |
38+
| **1** | `preset standard_options` | `{{presets.standard_options}}` | ✅ No conflicts, user-friendly |
39+
| 2 | `macro standard_options` | `{{macros.standard_options}}` | ✅ No conflicts, technical precision |
40+
| 3 | `def standard_options` | `{{defs.standard_options}}` | ✅ No conflicts, programming-friendly |
41+
| 4 | `definition standard_options` | `{{definitions.standard_options}}` | ✅ No conflicts, verbose |
42+
| 5 | `import standard_options` | `{{imports.standard_options}}` | ✅ No conflicts, developer-oriented |
43+
| 6 | `block standard_options` | `{{blocks.standard_options}}` | ⚠️ Potential confusion with Obsidian block refs |
44+
| 7 | `reference standard_options` | `{{references.standard_options}}` | ⚠️ Potential confusion with Obsidian links |
45+
| 8 | `ref standard_options` | `{{refs.standard_options}}` | ⚠️ Potential confusion with Obsidian links |
46+
| 9 | `include standard_options` | `{{includes.standard_options}}` | ❌ Conflicts with `description includes text` |
47+
| 10 | `snippet standard_options` | `{{snippets.standard_options}}` | ❌ Conflicts with Obsidian CSS snippets |
48+
| 11 | `template standard_options` | `{{templates.standard_options}}` | ❌ Strong conflict with Obsidian Templates |
49+
50+
## Rationale
51+
52+
"Preset" was selected as the optimal choice because:
53+
54+
1. **Zero Conflicts:** No collision with existing Tasks functionality or Obsidian features
55+
2. **User Accessibility:** Least programmer-centric terminology among viable options
56+
3. **Familiar Concept:** Widely used across many software applications (camera presets, filter presets, etc.)
57+
4. **Clear Intent:** Immediately conveys the concept of predefined configurations
58+
5. **Broad Appeal:** Accessible to users across all technical levels
59+
60+
The decision to use singular form in placeholder syntax (`{{preset.name}}` instead of `{{presets.name}}`) maintains consistency with other Tasks placeholder patterns.
61+
62+
## Consequences
63+
64+
### Positive
65+
66+
- Eliminates user confusion from naming collision
67+
- Provides clear, accessible terminology
68+
- Maintains feature functionality while improving usability
69+
- Establishes consistent naming pattern for future features
70+
71+
### Negative
72+
73+
- Requires updating developer's own test configurations (feature is pre-release)
74+
- Documentation and examples need updating before public release
75+
76+
### Neutral
77+
78+
- No user impact since feature is unreleased
79+
- Error messages and validation logic require updates
80+
81+
## Implementation Notes
82+
83+
- Update all pre-release documentation and examples to use new terminology
84+
- No user migration needed since feature is unreleased
85+
- Error messages and validation logic should use "preset" terminology
86+
- Release feature publicly with "preset" nomenclature
87+
88+
---
89+
90+
**Decision made by:** Clare Macrae
91+
**Date:** 2025-05-29
92+
**ADR Number:** 001
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
publish: true
3+
---
4+
5+
# About Architecture Decisions
6+
7+
## Introduction
8+
9+
This a collection of [Architecture decision records (ADRs)](https://github.com/joelparkerhenderson/architecture-decision-record) for the Tasks plugin.
10+
11+
If a design decision was non-obvious, we will record the thought process here.
12+
13+
- [[ADR-001 - Nomenclature for Reusable Instruction Blocks Feature]]

contributing/Welcome.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Getting the most from this documentation:
2020
- See [[About Testing]] to learn about automated and manual testing of the plugin
2121
- See [[About Debugging]] for tips to debug the plugin
2222
- See [[About Code]] for descriptions of the project's source code
23+
- See [[About Architecture Decisions]] for Architecture Decision Records
2324
- See [[About Documentation]] if you would like to improve and test the user docs
2425
- See [[About Translation]] if you are adding new text the plugin, and to help translate it
2526

docs/Scripting/Placeholders.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ Explanation of this Tasks code block query:
7575
7676
It is now possible to use properties in the query file. See [[Obsidian Properties#Using Query Properties in Searches]]
7777

78-
## Using includes.xxx
78+
## Using preset.xxx
7979

8080
You can do the following:
8181

8282
```text
83-
{{includes.my_snippet_from_settings}}
83+
{{preset.my_snippet_from_settings}}
8484
```
8585

86-
See also [[Includes]].
86+
See also [[Presets]].
8787

8888
## Error checking: invalid variables
8989

docs/Scripting/Includes.md renamed to docs/Scripting/Presets.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
publish: true
33
---
44

5-
# Includes
5+
# Presets
66

77
<span class="related-pages">#feature/scripting</span>
88

99
```text
10-
include my_snippet_from_settings
10+
preset my_snippet_from_settings
1111
```
1212

1313
You can only include full lines (valid instructions).

resources/sample_vaults/Tasks-Demo/.obsidian/plugins/obsidian-tasks-plugin/data.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"includes": {
2+
"presets": {
33
"hide_all_dates": "# Hide any values for all 6 date fields\nhide due date\nhide scheduled date\nhide start date\nhide created date\nhide done date\nhide cancelled date",
44
"hide_buttons": "# Hide postpone, edit and backinks\nhide postpone button\nhide edit button\nhide backlinks",
55
"hide_other_fields": "# Hide all the non-date fields, keep tags\nhide id\nhide depends on\nhide recurrence rule\nhide on completion\nhide priority",
6-
"just_the_description_and_tags": "# Show only description and any tags\ninclude hide_all_dates\ninclude hide_other_fields\ninclude hide_buttons",
7-
"just_the_description": "# Show only description, not even the tags\ninclude just_the_description_and_tags\nhide tags",
6+
"just_the_description_and_tags": "# Show only description and any tags\npreset hide_all_dates\npreset hide_other_fields\npreset hide_buttons",
7+
"just_the_description": "# Show only description, not even the tags\npreset just_the_description_and_tags\nhide tags",
88
"filter_by_context": "filter by function let fn = (ctx) => task.tags.includes(`#context/${ctx}`); return fn",
99
"extract_contexts_1": "ctx => task.tags.includes(`#context/${ctx}`)",
1010
"extract_contexts_2": "(ctx => task.tags.includes(`#context/${ctx}`))"

resources/sample_vaults/Tasks-Demo/How To/Reuse instructions across the vault.md

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ TQ_extra_instructions: |-
99

1010
These searches are for experimenting with, and understanding, the new "Includes" facility, which was released in Tasks X.Y.Z.
1111

12-
Includes values can be defined in the Tasks settings.
12+
presets values can be defined in the Tasks settings.
1313

1414
explain: `INPUT[toggle:TQ_explain]`
1515

@@ -19,12 +19,12 @@ explain: `INPUT[toggle:TQ_explain]`
1919

2020
````text
2121
```tasks
22-
include xxxx
22+
preset xxxx
2323
```
2424
````
2525

2626
```tasks
27-
include xxxx
27+
preset xxxx
2828
```
2929

3030
## Show all the fields
@@ -45,14 +45,14 @@ explain: `INPUT[toggle:TQ_explain]`
4545

4646
````text
4747
```tasks
48-
include hide_all_dates
49-
include hide_other_fields
48+
preset hide_all_dates
49+
preset hide_other_fields
5050
```
5151
````
5252

5353
```tasks
54-
include hide_all_dates
55-
include hide_other_fields
54+
preset hide_all_dates
55+
preset hide_other_fields
5656
```
5757

5858
## Hide all the Tasks user interface elements
@@ -61,12 +61,12 @@ explain: `INPUT[toggle:TQ_explain]`
6161

6262
````text
6363
```tasks
64-
include hide_buttons
64+
preset hide_buttons
6565
```
6666
````
6767

6868
```tasks
69-
include hide_buttons
69+
preset hide_buttons
7070
```
7171

7272
## Show only the description and any tags
@@ -75,12 +75,12 @@ explain: `INPUT[toggle:TQ_explain]`
7575

7676
````text
7777
```tasks
78-
include just_the_description_and_tags
78+
preset just_the_description_and_tags
7979
```
8080
````
8181

8282
```tasks
83-
include just_the_description_and_tags
83+
preset just_the_description_and_tags
8484
```
8585

8686
## Just show the description, without tags
@@ -89,12 +89,12 @@ explain: `INPUT[toggle:TQ_explain]`
8989

9090
````text
9191
```tasks
92-
include just_the_description
92+
preset just_the_description
9393
```
9494
````
9595

9696
```tasks
97-
include just_the_description
97+
preset just_the_description
9898
```
9999

100100
## Advanced use: return a function, that takes a parameter from the query source
@@ -106,17 +106,17 @@ explain: `INPUT[toggle:TQ_explain]`
106106
````text
107107
```tasks
108108
# For debug/explanatory purposes, show the source of the Include as a group name:
109-
group by function const x = "{{includes.filter_by_context}}"; return x
109+
group by function const x = "{{preset.filter_by_context}}"; return x
110110
111-
{{includes.filter_by_context}}('home')
111+
{{preset.filter_by_context}}('home')
112112
```
113113
````
114114

115115
```tasks
116116
# For debug/explanatory purposes, show the source of the Include as a group name:
117-
group by function const x = "{{includes.filter_by_context}}"; return x
117+
group by function const x = "{{preset.filter_by_context}}"; return x
118118
119-
{{includes.filter_by_context}}('home')
119+
{{preset.filter_by_context}}('home')
120120
```
121121

122122
### Has context 'home' - and group by the Include text - version 2
@@ -126,19 +126,19 @@ explain: `INPUT[toggle:TQ_explain]`
126126
````text
127127
```tasks
128128
# For debug/explanatory purposes, show the source of the Include as a group name:
129-
group by function const x = "{{includes.extract_contexts_1}}"; return x
129+
group by function const x = "{{preset.extract_contexts_1}}"; return x
130130
131131
# includes.extract_contexts_1 value needs to be surrounded by parentheses ():
132-
filter by function return ({{includes.extract_contexts_1}})('home')
132+
filter by function return ({{preset.extract_contexts_1}})('home')
133133
```
134134
````
135135

136136
```tasks
137137
# For debug/explanatory purposes, show the source of the Include as a group name:
138-
group by function const x = "{{includes.extract_contexts_1}}"; return x
138+
group by function const x = "{{preset.extract_contexts_1}}"; return x
139139
140140
# includes.extract_contexts_1 value needs to be surrounded by parentheses ():
141-
filter by function return ({{includes.extract_contexts_1}})('home')
141+
filter by function return ({{preset.extract_contexts_1}})('home')
142142
```
143143

144144
### Has context 'home' - and group by the Include text - version 3
@@ -148,17 +148,17 @@ explain: `INPUT[toggle:TQ_explain]`
148148
````text
149149
```tasks
150150
# For debug/explanatory purposes, show the source of the Include as a group name:
151-
group by function const x = "{{includes.extract_contexts_2}}"; return x
151+
group by function const x = "{{preset.extract_contexts_2}}"; return x
152152
153153
# includes.extract_contexts_1 value has the parentheses, to simplify use:
154-
filter by function {{includes.extract_contexts_2}}('home')
154+
filter by function {{preset.extract_contexts_2}}('home')
155155
```
156156
````
157157

158158
```tasks
159159
# For debug/explanatory purposes, show the source of the Include as a group name:
160-
group by function const x = "{{includes.extract_contexts_2}}"; return x
160+
group by function const x = "{{preset.extract_contexts_2}}"; return x
161161
162162
# includes.extract_contexts_1 value has the parentheses, to simplify use:
163-
filter by function {{includes.extract_contexts_2}}('home')
163+
filter by function {{preset.extract_contexts_2}}('home')
164164
```

src/Config/IncludesSettingsUI.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class IncludesSettingsUI {
4141
// Clear the input map when re-rendering
4242
this.nameFields.clear();
4343

44-
Object.entries(settings.includes).forEach(([key, value]) => {
44+
Object.entries(settings.presets).forEach(([key, value]) => {
4545
this.renderIncludeItem(includesContainer, settings, key, value, renderIncludes);
4646
});
4747
};
@@ -94,7 +94,7 @@ export class IncludesSettingsUI {
9494
// Handle renaming an include
9595
const commitRename = async () => {
9696
if (newKey && newKey !== key) {
97-
const updatedIncludes = this.includesSettingsService.renameInclude(settings.includes, key, newKey);
97+
const updatedIncludes = this.includesSettingsService.renameInclude(settings.presets, key, newKey);
9898
if (updatedIncludes) {
9999
await this.saveIncludesSettings(updatedIncludes, settings, refreshView);
100100
}
@@ -119,7 +119,7 @@ export class IncludesSettingsUI {
119119

120120
return textArea.onChange(async (newValue) => {
121121
const updatedIncludes = this.includesSettingsService.updateIncludeValue(
122-
settings.includes,
122+
settings.presets,
123123
key,
124124
newValue,
125125
);
@@ -147,7 +147,7 @@ export class IncludesSettingsUI {
147147
btn.setIcon('cross')
148148
.setTooltip('Delete')
149149
.onClick(async () => {
150-
const updatedIncludes = this.includesSettingsService.deleteInclude(settings.includes, key);
150+
const updatedIncludes = this.includesSettingsService.deleteInclude(settings.presets, key);
151151
await this.saveIncludesSettings(updatedIncludes, settings, refreshView);
152152
});
153153
});
@@ -225,7 +225,7 @@ export class IncludesSettingsUI {
225225

226226
// Perform the reorder
227227
const updatedIncludes = this.includesSettingsService.reorderInclude(
228-
settings.includes,
228+
settings.presets,
229229
draggedKey,
230230
targetIndex,
231231
);
@@ -246,7 +246,7 @@ export class IncludesSettingsUI {
246246
*/
247247
private getTargetIndex(targetKey: string, position: 'above' | 'below'): number {
248248
const settings = getSettings();
249-
const keys = Object.keys(settings.includes);
249+
const keys = Object.keys(settings.presets);
250250
const targetIndex = keys.indexOf(targetKey);
251251

252252
if (position === 'above') {
@@ -361,7 +361,7 @@ export class IncludesSettingsUI {
361361
btn.setButtonText('Add new include')
362362
.setCta()
363363
.onClick(async () => {
364-
const { includes: updatedIncludes } = this.includesSettingsService.addInclude(settings.includes);
364+
const { includes: updatedIncludes } = this.includesSettingsService.addInclude(settings.presets);
365365
await this.saveIncludesSettings(updatedIncludes, settings, refreshView);
366366
});
367367
});
@@ -380,11 +380,11 @@ export class IncludesSettingsUI {
380380
) {
381381
// TODO Consider how this relates to the validation code - should it refuse to save settings if validation fails?
382382
// Update the settings in storage
383-
updateSettings({ includes: updatedIncludes });
383+
updateSettings({ presets: updatedIncludes });
384384
await this.plugin.saveSettings();
385385

386386
// Update the local settings object to reflect the changes
387-
settings.includes = { ...updatedIncludes };
387+
settings.presets = { ...updatedIncludes };
388388

389389
// Refresh the view if a callback was provided
390390
if (refreshView) {

0 commit comments

Comments
 (0)