Skip to content

Commit 2c6cb6b

Browse files
authored
Merge pull request #3661 from ilandikov/feat-copy-results-improvements
feat: copy results improvements
2 parents af3a256 + 9b09f77 commit 2c6cb6b

File tree

9 files changed

+355
-7
lines changed

9 files changed

+355
-7
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- [ ] hyphen
2+
* [ ] asterisk
3+
+ [ ] plus
4+
1. [ ] numbered task

src/Query/Group/TaskGroup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export class TaskGroup {
106106
for (const heading of this.groupHeadings) {
107107
// These headings mimic the behaviour of QueryRenderer,
108108
// which uses 'h4', 'h5' and 'h6' for nested groups.
109-
const headingPrefix = '#'.repeat(4 + heading.nestingLevel);
109+
const headingPrefix = '#'.repeat(Math.min(4 + heading.nestingLevel, 6));
110110
output += `${headingPrefix} ${heading.displayName}\n\n`;
111111
}
112112

src/Query/QueryResult.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Task } from '../Task/Task';
12
import { TaskGroups } from './Group/TaskGroups';
23
import type { TaskGroup } from './Group/TaskGroup';
34
import { SearchInfo } from './SearchInfo';
@@ -48,4 +49,46 @@ export class QueryResult {
4849
result._searchErrorMessage = message;
4950
return result;
5051
}
52+
53+
public asMarkdown(): string {
54+
let markdown = '';
55+
56+
markdown += this.taskGroups.groups // force line break
57+
.map((group) => this.toString(group))
58+
.join('');
59+
60+
return markdown;
61+
}
62+
63+
private toString(group: TaskGroup): string {
64+
let output = '\n';
65+
66+
for (const heading of group.groupHeadings) {
67+
// These headings mimic the behaviour of QueryRenderer,
68+
// which uses 'h4', 'h5' and 'h6' for nested groups.
69+
const headingPrefix = '#'.repeat(Math.min(4 + heading.nestingLevel, 6));
70+
output += `${headingPrefix} ${heading.displayName}\n\n`;
71+
}
72+
73+
output += this.tasksAsStringOfLines(group.tasks);
74+
return output;
75+
}
76+
77+
private tasksAsStringOfLines(tasks: Task[]): string {
78+
let output = '';
79+
for (const task of tasks) {
80+
output += this.toFileLineString(task) + '\n';
81+
}
82+
return output;
83+
}
84+
85+
/**
86+
* This is a duplicate of Task.toFileLineString() because tasks rendered in search results
87+
* do not necessarily have the same indentation and list markers as the source task lines.
88+
*
89+
* @param task
90+
*/
91+
public toFileLineString(task: Task): string {
92+
return `- [${task.status.symbol}] ${task.toString()}`;
93+
}
5194
}

src/Renderer/QueryResultsRenderer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export class QueryResultsRenderer {
202202
const measureRender = new PerformanceTracker(`Render: ${this.query.queryId} - ${this.filePath}`);
203203
measureRender.start();
204204

205-
this.addCopyButton(content, queryResult.taskGroups);
205+
this.addCopyButton(content, queryResult);
206206

207207
await this.addAllTaskGroups(queryResult.taskGroups, content, queryRendererParameters);
208208

@@ -237,12 +237,12 @@ export class QueryResultsRenderer {
237237
content.appendChild(explanationsBlock);
238238
}
239239

240-
private addCopyButton(content: HTMLDivElement, taskGroups: TaskGroups) {
240+
private addCopyButton(content: HTMLDivElement, queryResult: QueryResult) {
241241
const copyButton = createAndAppendElement('button', content);
242242
copyButton.textContent = 'Copy results';
243243
copyButton.classList.add('plugin-tasks-copy-button');
244244
copyButton.addEventListener('click', async () => {
245-
await navigator.clipboard.writeText(taskGroups.toString());
245+
await navigator.clipboard.writeText(queryResult.asMarkdown());
246246
new Notice('Results copied to clipboard');
247247
});
248248
}

tests/Obsidian/AllCacheSampleData.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export type MockDataName =
5454
| 'links_everywhere'
5555
| 'list_statuses'
5656
| 'list_styles'
57+
| 'mixed_list_markers'
5758
| 'multi_line_task_and_list_item'
5859
| 'multiple_headings'
5960
| 'no_heading'
@@ -166,6 +167,7 @@ export const AllMockDataNames: MockDataName[] = [
166167
'links_everywhere',
167168
'list_statuses',
168169
'list_styles',
170+
'mixed_list_markers',
169171
'multi_line_task_and_list_item',
170172
'multiple_headings',
171173
'no_heading',

tests/Obsidian/__test_data__/metadataCache/resolvedLinks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@
228228
},
229229
"Test Data/list_statuses.md": {},
230230
"Test Data/list_styles.md": {},
231+
"Test Data/mixed_list_markers.md": {},
231232
"Test Data/multi_line_task_and_list_item.md": {},
232233
"Test Data/multiple_headings.md": {},
233234
"Test Data/no_heading.md": {},

tests/Obsidian/__test_data__/metadataCache/unresolvedLinks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
"Test Data/links_everywhere.md": {},
163163
"Test Data/list_statuses.md": {},
164164
"Test Data/list_styles.md": {},
165+
"Test Data/mixed_list_markers.md": {},
165166
"Test Data/multi_line_task_and_list_item.md": {},
166167
"Test Data/multiple_headings.md": {},
167168
"Test Data/no_heading.md": {},
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
{
2+
"cachedMetadata": {
3+
"listItems": [
4+
{
5+
"parent": -1,
6+
"position": {
7+
"end": {
8+
"col": 12,
9+
"line": 0,
10+
"offset": 12
11+
},
12+
"start": {
13+
"col": 0,
14+
"line": 0,
15+
"offset": 0
16+
}
17+
},
18+
"task": " "
19+
},
20+
{
21+
"parent": -1,
22+
"position": {
23+
"end": {
24+
"col": 14,
25+
"line": 1,
26+
"offset": 27
27+
},
28+
"start": {
29+
"col": 0,
30+
"line": 1,
31+
"offset": 13
32+
}
33+
},
34+
"task": " "
35+
},
36+
{
37+
"parent": -2,
38+
"position": {
39+
"end": {
40+
"col": 10,
41+
"line": 2,
42+
"offset": 38
43+
},
44+
"start": {
45+
"col": 0,
46+
"line": 2,
47+
"offset": 28
48+
}
49+
},
50+
"task": " "
51+
},
52+
{
53+
"parent": -3,
54+
"position": {
55+
"end": {
56+
"col": 20,
57+
"line": 3,
58+
"offset": 59
59+
},
60+
"start": {
61+
"col": 0,
62+
"line": 3,
63+
"offset": 39
64+
}
65+
},
66+
"task": " "
67+
}
68+
],
69+
"sections": [
70+
{
71+
"position": {
72+
"end": {
73+
"col": 12,
74+
"line": 0,
75+
"offset": 12
76+
},
77+
"start": {
78+
"col": 0,
79+
"line": 0,
80+
"offset": 0
81+
}
82+
},
83+
"type": "list"
84+
},
85+
{
86+
"position": {
87+
"end": {
88+
"col": 14,
89+
"line": 1,
90+
"offset": 27
91+
},
92+
"start": {
93+
"col": 0,
94+
"line": 1,
95+
"offset": 13
96+
}
97+
},
98+
"type": "list"
99+
},
100+
{
101+
"position": {
102+
"end": {
103+
"col": 10,
104+
"line": 2,
105+
"offset": 38
106+
},
107+
"start": {
108+
"col": 0,
109+
"line": 2,
110+
"offset": 28
111+
}
112+
},
113+
"type": "list"
114+
},
115+
{
116+
"position": {
117+
"end": {
118+
"col": 20,
119+
"line": 3,
120+
"offset": 59
121+
},
122+
"start": {
123+
"col": 0,
124+
"line": 3,
125+
"offset": 39
126+
}
127+
},
128+
"type": "list"
129+
}
130+
]
131+
},
132+
"fileContents": "- [ ] hyphen\n* [ ] asterisk\n+ [ ] plus\n1. [ ] numbered task\n",
133+
"filePath": "Test Data/mixed_list_markers.md",
134+
"getAllTags": [],
135+
"parseFrontMatterTags": null,
136+
"resolveLinkToPath": {}
137+
}

0 commit comments

Comments
 (0)