patram 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/patram.js +25 -147
- package/lib/build-graph-identity.js +238 -0
- package/lib/build-graph.js +143 -77
- package/lib/check-graph.js +23 -7
- package/lib/claim-helpers.js +55 -0
- package/lib/command-output.js +83 -0
- package/lib/layout-stored-queries.js +213 -0
- package/lib/list-queries.js +18 -0
- package/lib/list-source-files.js +50 -15
- package/lib/load-patram-config.js +106 -18
- package/lib/load-patram-config.types.ts +9 -0
- package/lib/load-project-graph.js +124 -0
- package/lib/output-view.types.ts +73 -0
- package/lib/parse-claims.js +38 -158
- package/lib/parse-claims.types.ts +7 -0
- package/lib/parse-cli-arguments-helpers.js +273 -0
- package/lib/parse-cli-arguments.js +114 -0
- package/lib/parse-cli-arguments.types.ts +24 -0
- package/lib/parse-cli-color-options.js +44 -0
- package/lib/parse-cli-query-pagination.js +49 -0
- package/lib/parse-jsdoc-blocks.js +184 -0
- package/lib/parse-jsdoc-claims.js +280 -0
- package/lib/parse-jsdoc-prose.js +111 -0
- package/lib/parse-markdown-claims.js +242 -0
- package/lib/parse-markdown-directives.js +136 -0
- package/lib/parse-where-clause.js +312 -0
- package/lib/patram-cli.js +337 -0
- package/lib/patram-config.js +3 -1
- package/lib/patram-config.types.ts +2 -1
- package/lib/query-graph.js +256 -0
- package/lib/render-check-output.js +315 -0
- package/lib/render-json-output.js +108 -0
- package/lib/render-output-view.js +193 -0
- package/lib/render-plain-output.js +237 -0
- package/lib/render-rich-output.js +293 -0
- package/lib/render-rich-source.js +1333 -0
- package/lib/resolve-check-target.js +190 -0
- package/lib/resolve-output-mode.js +60 -0
- package/lib/resolve-patram-graph-config.js +88 -0
- package/lib/resolve-where-clause.js +51 -0
- package/lib/show-document.js +311 -0
- package/lib/source-file-defaults.js +28 -0
- package/lib/write-paged-output.js +87 -0
- package/package.json +21 -10
- package/bin/patram.test.js +0 -184
- package/lib/build-graph.test.js +0 -141
- package/lib/check-graph.test.js +0 -103
- package/lib/list-source-files.test.js +0 -101
- package/lib/load-patram-config.test.js +0 -211
- package/lib/parse-claims.test.js +0 -113
- package/lib/patram-config.test.js +0 -147
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, ShowOutputView } from './output-view.types.ts';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { layoutStoredQueries } from './layout-stored-queries.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Render the canonical plain output for one output view.
|
|
9
|
+
*
|
|
10
|
+
* @param {OutputView} output_view
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function renderPlainOutput(output_view) {
|
|
14
|
+
if (output_view.command === 'query') {
|
|
15
|
+
return renderPlainQueryOutput(output_view);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (output_view.command === 'queries') {
|
|
19
|
+
return renderPlainStoredQueries(output_view.items);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (output_view.command === 'show') {
|
|
23
|
+
return renderPlainShowOutput(output_view);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
throw new Error('Unsupported output view command.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {QueryOutputView} output_view
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function renderPlainQueryOutput(output_view) {
|
|
34
|
+
const footer = renderPlainQueryFooter(output_view.summary, output_view.hints);
|
|
35
|
+
|
|
36
|
+
if (output_view.items.length === 0) {
|
|
37
|
+
return renderPlainEmptyQuery(footer);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (footer.length === 0) {
|
|
41
|
+
return `${output_view.items.map(formatPlainNodeItem).join('\n\n')}\n`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return `${output_view.items.map(formatPlainNodeItem).join('\n\n')}\n\n${footer}\n`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {string} footer
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function renderPlainEmptyQuery(footer) {
|
|
52
|
+
if (footer.length === 0) {
|
|
53
|
+
return 'No matches.\n';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `No matches.\n${footer}\n`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {OutputStoredQueryItem[]} output_items
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
function renderPlainStoredQueries(output_items) {
|
|
64
|
+
if (output_items.length === 0) {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return `${layoutStoredQueries(output_items)
|
|
69
|
+
.map(formatPlainStoredQueryLine)
|
|
70
|
+
.join('\n')}\n`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {ShowOutputView} output_view
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function renderPlainShowOutput(output_view) {
|
|
78
|
+
const rendered_source = trimTrailingLineBreaks(output_view.rendered_source);
|
|
79
|
+
|
|
80
|
+
if (output_view.items.length === 0) {
|
|
81
|
+
return `${rendered_source}\n`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return `${rendered_source}\n\n----------------\n${output_view.items.map(formatPlainResolvedLinkItem).join('\n\n')}\n`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {OutputNodeItem} output_item
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
function formatPlainNodeItem(output_item) {
|
|
92
|
+
const metadata_row = formatMetadataRow(output_item);
|
|
93
|
+
/** @type {string[]} */
|
|
94
|
+
const lines = [formatNodeHeader(output_item)];
|
|
95
|
+
|
|
96
|
+
if (metadata_row.length > 0) {
|
|
97
|
+
lines.push(metadata_row);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
lines.push('', ` ${output_item.title}`);
|
|
101
|
+
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {{ text: string }[]} line_segments
|
|
107
|
+
* @returns {string}
|
|
108
|
+
*/
|
|
109
|
+
function formatPlainStoredQueryLine(line_segments) {
|
|
110
|
+
return line_segments.map((segment) => segment.text).join('');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {OutputResolvedLinkItem} output_item
|
|
115
|
+
* @returns {string}
|
|
116
|
+
*/
|
|
117
|
+
function formatPlainResolvedLinkItem(output_item) {
|
|
118
|
+
const metadata_row = formatResolvedLinkMetadataRow(output_item.target);
|
|
119
|
+
/** @type {string[]} */
|
|
120
|
+
const lines = [
|
|
121
|
+
`[${output_item.reference}] document ${output_item.target.path}`,
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
if (metadata_row.length > 0) {
|
|
125
|
+
lines.push(` ${metadata_row}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
lines.push('', ` ${output_item.target.title}`);
|
|
129
|
+
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {OutputNodeItem} output_item
|
|
135
|
+
* @returns {string}
|
|
136
|
+
*/
|
|
137
|
+
function formatMetadataRow(output_item) {
|
|
138
|
+
/** @type {string[]} */
|
|
139
|
+
const metadata_fields = [];
|
|
140
|
+
|
|
141
|
+
if (isDocumentNode(output_item)) {
|
|
142
|
+
metadata_fields.push(`kind: ${output_item.node_kind}`);
|
|
143
|
+
} else {
|
|
144
|
+
metadata_fields.push(`path: ${output_item.path}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (output_item.status) {
|
|
148
|
+
metadata_fields.push(`status: ${output_item.status}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return metadata_fields.join(' ');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {OutputNodeItem} output_item
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
function formatNodeHeader(output_item) {
|
|
159
|
+
if (isDocumentNode(output_item)) {
|
|
160
|
+
return `document ${output_item.path}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return `${output_item.node_kind} ${output_item.id}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {OutputNodeItem} output_item
|
|
168
|
+
* @returns {boolean}
|
|
169
|
+
*/
|
|
170
|
+
function isDocumentNode(output_item) {
|
|
171
|
+
return output_item.id === `doc:${output_item.path}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @param {{ kind?: string, status?: string }} target
|
|
176
|
+
* @returns {string}
|
|
177
|
+
*/
|
|
178
|
+
function formatResolvedLinkMetadataRow(target) {
|
|
179
|
+
/** @type {string[]} */
|
|
180
|
+
const metadata_fields = [];
|
|
181
|
+
|
|
182
|
+
if (target.kind) {
|
|
183
|
+
metadata_fields.push(`kind: ${target.kind}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (target.status) {
|
|
187
|
+
metadata_fields.push(`status: ${target.status}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return metadata_fields.join(' ');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @param {string} value
|
|
195
|
+
* @returns {string}
|
|
196
|
+
*/
|
|
197
|
+
function trimTrailingLineBreaks(value) {
|
|
198
|
+
return value.replace(/\n+$/du, '');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @param {{ count: number, limit: number, offset: number, total_count: number }} summary
|
|
203
|
+
* @returns {string}
|
|
204
|
+
*/
|
|
205
|
+
function formatQuerySummary(summary) {
|
|
206
|
+
if (!shouldRenderQuerySummary(summary)) {
|
|
207
|
+
return '';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return `Showing ${summary.count} of ${summary.total_count} matches.`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {{ count: number, limit: number, offset: number, total_count: number }} summary
|
|
215
|
+
* @param {string[]} hints
|
|
216
|
+
* @returns {string}
|
|
217
|
+
*/
|
|
218
|
+
function renderPlainQueryFooter(summary, hints) {
|
|
219
|
+
const summary_line = formatQuerySummary(summary);
|
|
220
|
+
const footer_lines = [];
|
|
221
|
+
|
|
222
|
+
if (summary_line.length > 0) {
|
|
223
|
+
footer_lines.push(summary_line);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
footer_lines.push(...hints);
|
|
227
|
+
|
|
228
|
+
return footer_lines.join('\n');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {{ limit: number, offset: number, total_count: number }} summary
|
|
233
|
+
* @returns {boolean}
|
|
234
|
+
*/
|
|
235
|
+
function shouldRenderQuerySummary(summary) {
|
|
236
|
+
return summary.offset > 0 || summary.total_count > summary.limit;
|
|
237
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { CliColorMode } from './parse-cli-arguments.types.ts';
|
|
3
|
+
* @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, ShowOutputView } from './output-view.types.ts';
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Ansis } from 'ansis';
|
|
7
|
+
|
|
8
|
+
import { layoutStoredQueries } from './layout-stored-queries.js';
|
|
9
|
+
import { renderRichSource } from './render-rich-source.js';
|
|
10
|
+
|
|
11
|
+
const FULL_WIDTH_DIVIDER = ` ${'─'.repeat(78)} `;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render styled rich output while preserving the plain layout.
|
|
15
|
+
*
|
|
16
|
+
* @param {OutputView} output_view
|
|
17
|
+
* @param {{ color_mode: CliColorMode, color_enabled: boolean }} render_options
|
|
18
|
+
* @returns {Promise<string>}
|
|
19
|
+
*/
|
|
20
|
+
export async function renderRichOutput(output_view, render_options) {
|
|
21
|
+
const ansi = createAnsi(render_options.color_enabled);
|
|
22
|
+
|
|
23
|
+
if (output_view.command === 'query') {
|
|
24
|
+
return renderRichQueryOutput(output_view, ansi);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (output_view.command === 'queries') {
|
|
28
|
+
return renderRichStoredQueries(output_view.items, ansi);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (output_view.command === 'show') {
|
|
32
|
+
return renderRichShowOutput(output_view, render_options, ansi);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
throw new Error('Unsupported output view command.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {QueryOutputView} output_view
|
|
40
|
+
* @param {Ansis} ansi
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
function renderRichQueryOutput(output_view, ansi) {
|
|
44
|
+
const footer = renderRichQueryFooter(
|
|
45
|
+
output_view.summary,
|
|
46
|
+
output_view.hints,
|
|
47
|
+
ansi,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (output_view.items.length === 0) {
|
|
51
|
+
return renderRichEmptyQuery(footer, ansi);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (footer.length === 0) {
|
|
55
|
+
return `${output_view.items.map((item) => formatRichNodeItem(item, ansi)).join('\n\n')}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `${output_view.items.map((item) => formatRichNodeItem(item, ansi)).join('\n\n')}\n\n${footer}\n`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} footer
|
|
63
|
+
* @param {Ansis} ansi
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
function renderRichEmptyQuery(footer, ansi) {
|
|
67
|
+
if (footer.length === 0) {
|
|
68
|
+
return `${ansi.yellow('No matches.')}\n`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return `${ansi.yellow('No matches.')}\n${footer}\n`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {OutputStoredQueryItem[]} output_items
|
|
76
|
+
* @param {Ansis} ansi
|
|
77
|
+
* @returns {string}
|
|
78
|
+
*/
|
|
79
|
+
function renderRichStoredQueries(output_items, ansi) {
|
|
80
|
+
if (output_items.length === 0) {
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return `${layoutStoredQueries(output_items)
|
|
85
|
+
.map((line_segments) => formatRichStoredQueryLine(line_segments, ansi))
|
|
86
|
+
.join('\n')}\n`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {ShowOutputView} output_view
|
|
91
|
+
* @param {{ color_mode: CliColorMode, color_enabled: boolean }} render_options
|
|
92
|
+
* @param {Ansis} ansi
|
|
93
|
+
* @returns {Promise<string>}
|
|
94
|
+
*/
|
|
95
|
+
async function renderRichShowOutput(output_view, render_options, ansi) {
|
|
96
|
+
const rendered_source = trimTrailingLineBreaks(
|
|
97
|
+
await renderRichSource(output_view, render_options),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (output_view.items.length === 0) {
|
|
101
|
+
return `${rendered_source}\n`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return `${rendered_source}\n\n${ansi.gray(FULL_WIDTH_DIVIDER)}\n\n${output_view.items.map((item) => formatRichResolvedLinkItem(item, ansi)).join('\n\n')}\n`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {OutputNodeItem} output_item
|
|
109
|
+
* @param {Ansis} ansi
|
|
110
|
+
* @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
function formatRichNodeItem(output_item, ansi) {
|
|
113
|
+
const metadata_row = formatRichMetadataRow(output_item);
|
|
114
|
+
/** @type {string[]} */
|
|
115
|
+
const lines = [ansi.green(formatNodeHeader(output_item))];
|
|
116
|
+
|
|
117
|
+
if (metadata_row.length > 0) {
|
|
118
|
+
lines.push(metadata_row);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
lines.push('', ` ${output_item.title}`);
|
|
122
|
+
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {{ kind: 'field_name' | 'keyword' | 'literal' | 'name' | 'operator' | 'plain', text: string }[]} line_segments
|
|
128
|
+
* @param {Ansis} ansi
|
|
129
|
+
* @returns {string}
|
|
130
|
+
*/
|
|
131
|
+
function formatRichStoredQueryLine(line_segments, ansi) {
|
|
132
|
+
return line_segments
|
|
133
|
+
.map((line_segment) => styleStoredQuerySegment(line_segment, ansi))
|
|
134
|
+
.join('');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {OutputResolvedLinkItem} output_item
|
|
139
|
+
* @param {Ansis} ansi
|
|
140
|
+
* @returns {string}
|
|
141
|
+
*/
|
|
142
|
+
function formatRichResolvedLinkItem(output_item, ansi) {
|
|
143
|
+
const metadata_row = formatRichResolvedLinkMetadataRow(output_item.target);
|
|
144
|
+
/** @type {string[]} */
|
|
145
|
+
const lines = [
|
|
146
|
+
`${ansi.gray(`[${output_item.reference}]`)} ${ansi.green(`document ${output_item.target.path}`)}`,
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
if (metadata_row.length > 0) {
|
|
150
|
+
lines.push(` ${metadata_row}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
lines.push('', ` ${output_item.target.title}`);
|
|
154
|
+
|
|
155
|
+
return lines.join('\n');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {OutputNodeItem} output_item
|
|
160
|
+
* @returns {string}
|
|
161
|
+
*/
|
|
162
|
+
function formatRichMetadataRow(output_item) {
|
|
163
|
+
/** @type {string[]} */
|
|
164
|
+
const metadata_fields = [];
|
|
165
|
+
|
|
166
|
+
if (isDocumentNode(output_item)) {
|
|
167
|
+
metadata_fields.push(`kind: ${output_item.node_kind}`);
|
|
168
|
+
} else {
|
|
169
|
+
metadata_fields.push(`path: ${output_item.path}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (output_item.status) {
|
|
173
|
+
metadata_fields.push(`status: ${output_item.status}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return metadata_fields.join(' ');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {{ kind?: string, status?: string }} target
|
|
181
|
+
* @returns {string}
|
|
182
|
+
*/
|
|
183
|
+
function formatRichResolvedLinkMetadataRow(target) {
|
|
184
|
+
/** @type {string[]} */
|
|
185
|
+
const metadata_fields = [];
|
|
186
|
+
|
|
187
|
+
if (target.kind) {
|
|
188
|
+
metadata_fields.push(`kind: ${target.kind}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (target.status) {
|
|
192
|
+
metadata_fields.push(`status: ${target.status}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return metadata_fields.join(' ');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {boolean} color_enabled
|
|
200
|
+
* @returns {Ansis}
|
|
201
|
+
*/
|
|
202
|
+
function createAnsi(color_enabled) {
|
|
203
|
+
return new Ansis(color_enabled ? 3 : 0);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {{ kind: 'field_name' | 'keyword' | 'literal' | 'name' | 'operator' | 'plain', text: string }} line_segment
|
|
208
|
+
* @param {Ansis} ansi
|
|
209
|
+
* @returns {string}
|
|
210
|
+
*/
|
|
211
|
+
function styleStoredQuerySegment(line_segment, ansi) {
|
|
212
|
+
if (line_segment.kind === 'name') {
|
|
213
|
+
return ansi.green(line_segment.text);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (line_segment.kind === 'operator') {
|
|
217
|
+
return ansi.gray(line_segment.text);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (line_segment.kind === 'keyword') {
|
|
221
|
+
return ansi.yellow(line_segment.text);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return line_segment.text;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @param {OutputNodeItem} output_item
|
|
229
|
+
* @returns {string}
|
|
230
|
+
*/
|
|
231
|
+
function formatNodeHeader(output_item) {
|
|
232
|
+
if (isDocumentNode(output_item)) {
|
|
233
|
+
return `document ${output_item.path}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return `${output_item.node_kind} ${output_item.id}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @param {OutputNodeItem} output_item
|
|
241
|
+
* @returns {boolean}
|
|
242
|
+
*/
|
|
243
|
+
function isDocumentNode(output_item) {
|
|
244
|
+
return output_item.id === `doc:${output_item.path}`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {string} value
|
|
249
|
+
* @returns {string}
|
|
250
|
+
*/
|
|
251
|
+
function trimTrailingLineBreaks(value) {
|
|
252
|
+
return value.replace(/\n+$/du, '');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @param {{ count: number, limit: number, offset: number, total_count: number }} summary
|
|
257
|
+
* @returns {string}
|
|
258
|
+
*/
|
|
259
|
+
function formatQuerySummary(summary) {
|
|
260
|
+
if (!shouldRenderQuerySummary(summary)) {
|
|
261
|
+
return '';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return `Showing ${summary.count} of ${summary.total_count} matches.`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @param {{ count: number, limit: number, offset: number, total_count: number }} summary
|
|
269
|
+
* @param {string[]} hints
|
|
270
|
+
* @param {Ansis} ansi
|
|
271
|
+
* @returns {string}
|
|
272
|
+
*/
|
|
273
|
+
function renderRichQueryFooter(summary, hints, ansi) {
|
|
274
|
+
const summary_line = formatQuerySummary(summary);
|
|
275
|
+
/** @type {string[]} */
|
|
276
|
+
const footer_lines = [];
|
|
277
|
+
|
|
278
|
+
if (summary_line.length > 0) {
|
|
279
|
+
footer_lines.push(summary_line);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
footer_lines.push(...hints.map((hint) => ansi.gray(hint)));
|
|
283
|
+
|
|
284
|
+
return footer_lines.join('\n');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @param {{ limit: number, offset: number, total_count: number }} summary
|
|
289
|
+
* @returns {boolean}
|
|
290
|
+
*/
|
|
291
|
+
function shouldRenderQuerySummary(summary) {
|
|
292
|
+
return summary.offset > 0 || summary.total_count > summary.limit;
|
|
293
|
+
}
|