Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/environment/node/argv.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import minimist from 'minimist';
7
import { isWindows } from '../../../base/common/platform.js';
8
import { localize } from '../../../nls.js';
9
import { NativeParsedArgs } from '../common/argv.js';
10
11
/**
12
* This code is also used by standalone cli's. Avoid adding any other dependencies.
13
*/
14
const helpCategories = {
15
o: localize('optionsUpperCase', "Options"),
16
e: localize('extensionsManagement', "Extensions Management"),
17
t: localize('troubleshooting', "Troubleshooting"),
18
m: localize('mcp', "Model Context Protocol")
19
};
20
21
export interface Option<OptionType> {
22
type: OptionType;
23
alias?: string;
24
deprecates?: string[]; // old deprecated ids
25
args?: string | string[];
26
description?: string;
27
deprecationMessage?: string;
28
allowEmptyValue?: boolean;
29
cat?: keyof typeof helpCategories;
30
global?: boolean;
31
}
32
33
export interface Subcommand<T> {
34
type: 'subcommand';
35
description?: string;
36
deprecationMessage?: string;
37
options: OptionDescriptions<Required<T>>;
38
}
39
40
export type OptionDescriptions<T> = {
41
[P in keyof T]:
42
T[P] extends boolean | undefined ? Option<'boolean'> :
43
T[P] extends string | undefined ? Option<'string'> :
44
T[P] extends string[] | undefined ? Option<'string[]'> :
45
Subcommand<T[P]>
46
};
47
48
export const NATIVE_CLI_COMMANDS = ['tunnel', 'serve-web'] as const;
49
50
export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
51
'chat': {
52
type: 'subcommand',
53
description: 'Pass in a prompt to run in a chat session in the current working directory.',
54
options: {
55
'_': { type: 'string[]', description: localize('prompt', "The prompt to use as chat.") },
56
'mode': { type: 'string', cat: 'o', alias: 'm', args: 'mode', description: localize('chatMode', "The mode to use for the chat session. Available options: 'ask', 'edit', 'agent', or the identifier of a custom mode. Defaults to 'agent'.") },
57
'add-file': { type: 'string[]', cat: 'o', alias: 'a', args: 'path', description: localize('addFile', "Add files as context to the chat session.") },
58
'maximize': { type: 'boolean', cat: 'o', description: localize('chatMaximize', "Maximize the chat session view.") },
59
'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindowForChat', "Force to use the last active window for the chat session.") },
60
'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindowForChat', "Force to open an empty window for the chat session.") },
61
'profile': { type: 'string', 'cat': 'o', args: 'profileName', description: localize('profileName', "Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created.") },
62
'help': { type: 'boolean', alias: 'h', description: localize('help', "Print usage.") }
63
}
64
},
65
'serve-web': {
66
type: 'subcommand',
67
description: 'Run a server that displays the editor UI in browsers.',
68
options: {
69
'cli-data-dir': { type: 'string', args: 'dir', description: localize('cliDataDir', "Directory where CLI metadata should be stored.") },
70
'disable-telemetry': { type: 'boolean' },
71
'telemetry-level': { type: 'string' },
72
}
73
},
74
'tunnel': {
75
type: 'subcommand',
76
description: 'Make the current machine accessible from vscode.dev or other machines through a secure tunnel.',
77
options: {
78
'cli-data-dir': { type: 'string', args: 'dir', description: localize('cliDataDir', "Directory where CLI metadata should be stored.") },
79
'disable-telemetry': { type: 'boolean' },
80
'telemetry-level': { type: 'string' },
81
user: {
82
type: 'subcommand',
83
options: {
84
login: {
85
type: 'subcommand',
86
options: {
87
provider: { type: 'string' },
88
'access-token': { type: 'string' }
89
}
90
}
91
}
92
}
93
}
94
},
95
'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") },
96
'merge': { type: 'boolean', cat: 'o', alias: 'm', args: ['path1', 'path2', 'base', 'result'], description: localize('merge', "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.") },
97
'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") },
98
'remove': { type: 'boolean', cat: 'o', args: 'folder', description: localize('remove', "Remove folder(s) from the last active window.") },
99
'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") },
100
'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") },
101
'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") },
102
'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") },
103
'waitMarkerFilePath': { type: 'string' },
104
'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") },
105
'user-data-dir': { type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") },
106
'profile': { type: 'string', 'cat': 'o', args: 'profileName', description: localize('profileName', "Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created.") },
107
'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
108
109
'extensions-dir': { type: 'string', deprecates: ['extensionHomePath'], cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
110
'extensions-download-dir': { type: 'string' },
111
'builtin-extensions-dir': { type: 'string' },
112
'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
113
'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extensions.") },
114
'category': { type: 'string', allowEmptyValue: true, cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extensions."), args: 'category' },
115
'install-extension': { type: 'string[]', cat: 'e', args: 'ext-id | path', description: localize('installExtension', "Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '${publisher}.${name}'. Use '--force' argument to update to latest version. To install a specific version provide '@${version}'. For example: '[email protected]'.") },
116
'pre-release': { type: 'boolean', cat: 'e', description: localize('install prerelease', "Installs the pre-release version of the extension, when using --install-extension") },
117
'uninstall-extension': { type: 'string[]', cat: 'e', args: 'ext-id', description: localize('uninstallExtension', "Uninstalls an extension.") },
118
'update-extensions': { type: 'boolean', cat: 'e', description: localize('updateExtensions', "Update the installed extensions.") },
119
'enable-proposed-api': { type: 'string[]', allowEmptyValue: true, cat: 'e', args: 'ext-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") },
120
121
'add-mcp': { type: 'string[]', cat: 'm', args: 'json', description: localize('addMcp', "Adds a Model Context Protocol server definition to the user profile. Accepts JSON input in the form '{\"name\":\"server-name\",\"command\":...}'") },
122
123
'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") },
124
'verbose': { type: 'boolean', cat: 't', global: true, description: localize('verbose', "Print verbose output (implies --wait).") },
125
'log': { type: 'string[]', cat: 't', args: 'level', global: true, description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. You can also configure the log level of an extension by passing extension id and log level in the following format: '${publisher}.${name}:${logLevel}'. For example: 'vscode.csharp:trace'. Can receive one or more such entries.") },
126
'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
127
'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup.") },
128
'prof-append-timers': { type: 'string' },
129
'prof-duration-markers': { type: 'string[]' },
130
'prof-duration-markers-file': { type: 'string' },
131
'no-cached-data': { type: 'boolean' },
132
'prof-startup-prefix': { type: 'string' },
133
'prof-v8-extensions': { type: 'boolean' },
134
'disable-extensions': { type: 'boolean', deprecates: ['disableExtensions'], cat: 't', description: localize('disableExtensions', "Disable all installed extensions. This option is not persisted and is effective only when the command opens a new window.") },
135
'disable-extension': { type: 'string[]', cat: 't', args: 'ext-id', description: localize('disableExtension', "Disable the provided extension. This option is not persisted and is effective only when the command opens a new window.") },
136
'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off."), args: ['on | off'] },
137
138
'inspect-extensions': { type: 'string', allowEmptyValue: true, deprecates: ['debugPluginHost'], args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") },
139
'inspect-brk-extensions': { type: 'string', allowEmptyValue: true, deprecates: ['debugBrkPluginHost'], args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") },
140
'disable-lcd-text': { type: 'boolean', cat: 't', description: localize('disableLCDText', "Disable LCD font rendering.") },
141
'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
142
'disable-chromium-sandbox': { type: 'boolean', cat: 't', description: localize('disableChromiumSandbox', "Use this option only when there is requirement to launch the application as sudo user on Linux or when running as an elevated user in an applocker environment on Windows.") },
143
'sandbox': { type: 'boolean' },
144
'locate-shell-integration-path': { type: 'string', cat: 't', args: ['shell'], description: localize('locateShellIntegrationPath', "Print the path to a terminal shell integration script. Allowed values are 'bash', 'pwsh', 'zsh' or 'fish'.") },
145
'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
146
147
'remote': { type: 'string', allowEmptyValue: true },
148
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri' },
149
'file-uri': { type: 'string[]', cat: 'o', args: 'uri' },
150
151
'locate-extension': { type: 'string[]' },
152
'extensionDevelopmentPath': { type: 'string[]' },
153
'extensionDevelopmentKind': { type: 'string[]' },
154
'extensionTestsPath': { type: 'string' },
155
'extensionEnvironment': { type: 'string' },
156
'debugId': { type: 'string' },
157
'debugRenderer': { type: 'boolean' },
158
'inspect-ptyhost': { type: 'string', allowEmptyValue: true },
159
'inspect-brk-ptyhost': { type: 'string', allowEmptyValue: true },
160
'inspect-search': { type: 'string', deprecates: ['debugSearch'], allowEmptyValue: true },
161
'inspect-brk-search': { type: 'string', deprecates: ['debugBrkSearch'], allowEmptyValue: true },
162
'inspect-sharedprocess': { type: 'string', allowEmptyValue: true },
163
'inspect-brk-sharedprocess': { type: 'string', allowEmptyValue: true },
164
'export-default-configuration': { type: 'string' },
165
'install-source': { type: 'string' },
166
'enable-smoke-test-driver': { type: 'boolean' },
167
'logExtensionHostCommunication': { type: 'boolean' },
168
'skip-release-notes': { type: 'boolean' },
169
'skip-welcome': { type: 'boolean' },
170
'disable-telemetry': { type: 'boolean' },
171
'disable-updates': { type: 'boolean' },
172
'transient': { type: 'boolean', cat: 't', description: localize('transient', "Run with temporary data and extension directories, as if launched for the first time.") },
173
'use-inmemory-secretstorage': { type: 'boolean', deprecates: ['disable-keytar'] },
174
'password-store': { type: 'string' },
175
'disable-workspace-trust': { type: 'boolean' },
176
'disable-crash-reporter': { type: 'boolean' },
177
'crash-reporter-directory': { type: 'string' },
178
'crash-reporter-id': { type: 'string' },
179
'skip-add-to-recently-opened': { type: 'boolean' },
180
'open-url': { type: 'boolean' },
181
'file-write': { type: 'boolean' },
182
'file-chmod': { type: 'boolean' },
183
'install-builtin-extension': { type: 'string[]' },
184
'force': { type: 'boolean' },
185
'do-not-sync': { type: 'boolean' },
186
'do-not-include-pack-dependencies': { type: 'boolean' },
187
'trace': { type: 'boolean' },
188
'trace-memory-infra': { type: 'boolean' },
189
'trace-category-filter': { type: 'string' },
190
'trace-options': { type: 'string' },
191
'preserve-env': { type: 'boolean' },
192
'force-user-env': { type: 'boolean' },
193
'force-disable-user-env': { type: 'boolean' },
194
'open-devtools': { type: 'boolean' },
195
'disable-gpu-sandbox': { type: 'boolean' },
196
'logsPath': { type: 'string' },
197
'__enable-file-policy': { type: 'boolean' },
198
'editSessionId': { type: 'string' },
199
'continueOn': { type: 'string' },
200
'enable-coi': { type: 'boolean' },
201
'unresponsive-sample-interval': { type: 'string' },
202
'unresponsive-sample-period': { type: 'string' },
203
'enable-rdp-display-tracking': { type: 'boolean' },
204
'disable-layout-restore': { type: 'boolean' },
205
'disable-experiments': { type: 'boolean' },
206
207
// chromium flags
208
'no-proxy-server': { type: 'boolean' },
209
// Minimist incorrectly parses keys that start with `--no`
210
// https://github.com/substack/minimist/blob/aeb3e27dae0412de5c0494e9563a5f10c82cc7a9/index.js#L118-L121
211
// If --no-sandbox is passed via cli wrapper it will be treated as --sandbox which is incorrect, we use
212
// the alias here to make sure --no-sandbox is always respected.
213
// For https://github.com/microsoft/vscode/issues/128279
214
'no-sandbox': { type: 'boolean', alias: 'sandbox' },
215
'proxy-server': { type: 'string' },
216
'proxy-bypass-list': { type: 'string' },
217
'proxy-pac-url': { type: 'string' },
218
'js-flags': { type: 'string' }, // chrome js flags
219
'inspect': { type: 'string', allowEmptyValue: true },
220
'inspect-brk': { type: 'string', allowEmptyValue: true },
221
'nolazy': { type: 'boolean' }, // node inspect
222
'force-device-scale-factor': { type: 'string' },
223
'force-renderer-accessibility': { type: 'boolean' },
224
'ignore-certificate-errors': { type: 'boolean' },
225
'allow-insecure-localhost': { type: 'boolean' },
226
'log-net-log': { type: 'string' },
227
'vmodule': { type: 'string' },
228
'_urls': { type: 'string[]' },
229
'disable-dev-shm-usage': { type: 'boolean' },
230
'profile-temp': { type: 'boolean' },
231
'ozone-platform': { type: 'string' },
232
'enable-tracing': { type: 'string' },
233
'trace-startup-format': { type: 'string' },
234
'trace-startup-file': { type: 'string' },
235
'trace-startup-duration': { type: 'string' },
236
'xdg-portal-required-version': { type: 'string' },
237
238
_: { type: 'string[]' } // main arguments
239
};
240
241
export interface ErrorReporter {
242
onUnknownOption(id: string): void;
243
onMultipleValues(id: string, usedValue: string): void;
244
onEmptyValue(id: string): void;
245
onDeprecatedOption(deprecatedId: string, message: string): void;
246
247
getSubcommandReporter?(command: string): ErrorReporter;
248
}
249
250
const ignoringReporter = {
251
onUnknownOption: () => { },
252
onMultipleValues: () => { },
253
onEmptyValue: () => { },
254
onDeprecatedOption: () => { }
255
};
256
257
export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, errorReporter: ErrorReporter = ignoringReporter): T {
258
// Find the first non-option arg, which also isn't the value for a previous `--flag`
259
const firstPossibleCommand = args.find((a, i) => a.length > 0 && a[0] !== '-' && options.hasOwnProperty(a) && options[a as T].type === 'subcommand');
260
261
const alias: { [key: string]: string } = {};
262
const stringOptions: string[] = ['_'];
263
const booleanOptions: string[] = [];
264
const globalOptions: OptionDescriptions<any> = {};
265
let command: Subcommand<any> | undefined = undefined;
266
for (const optionId in options) {
267
const o = options[optionId];
268
if (o.type === 'subcommand') {
269
if (optionId === firstPossibleCommand) {
270
command = o;
271
}
272
} else {
273
if (o.alias) {
274
alias[optionId] = o.alias;
275
}
276
277
if (o.type === 'string' || o.type === 'string[]') {
278
stringOptions.push(optionId);
279
if (o.deprecates) {
280
stringOptions.push(...o.deprecates);
281
}
282
} else if (o.type === 'boolean') {
283
booleanOptions.push(optionId);
284
if (o.deprecates) {
285
booleanOptions.push(...o.deprecates);
286
}
287
}
288
if (o.global) {
289
globalOptions[optionId] = o;
290
}
291
}
292
}
293
if (command && firstPossibleCommand) {
294
const options = globalOptions;
295
for (const optionId in command.options) {
296
options[optionId] = command.options[optionId];
297
}
298
const newArgs = args.filter(a => a !== firstPossibleCommand);
299
const reporter = errorReporter.getSubcommandReporter ? errorReporter.getSubcommandReporter(firstPossibleCommand) : undefined;
300
const subcommandOptions = parseArgs(newArgs, options, reporter);
301
// eslint-disable-next-line local/code-no-dangerous-type-assertions
302
return <T>{
303
[firstPossibleCommand]: subcommandOptions,
304
_: []
305
};
306
}
307
308
309
// remove aliases to avoid confusion
310
const parsedArgs = minimist(args, { string: stringOptions, boolean: booleanOptions, alias });
311
312
const cleanedArgs: any = {};
313
const remainingArgs: any = parsedArgs;
314
315
// https://github.com/microsoft/vscode/issues/58177, https://github.com/microsoft/vscode/issues/106617
316
cleanedArgs._ = parsedArgs._.map(arg => String(arg)).filter(arg => arg.length > 0);
317
318
delete remainingArgs._;
319
320
for (const optionId in options) {
321
const o = options[optionId];
322
if (o.type === 'subcommand') {
323
continue;
324
}
325
if (o.alias) {
326
delete remainingArgs[o.alias];
327
}
328
329
let val = remainingArgs[optionId];
330
if (o.deprecates) {
331
for (const deprecatedId of o.deprecates) {
332
if (remainingArgs.hasOwnProperty(deprecatedId)) {
333
if (!val) {
334
val = remainingArgs[deprecatedId];
335
if (val) {
336
errorReporter.onDeprecatedOption(deprecatedId, o.deprecationMessage || localize('deprecated.useInstead', 'Use {0} instead.', optionId));
337
}
338
}
339
delete remainingArgs[deprecatedId];
340
}
341
}
342
}
343
344
if (typeof val !== 'undefined') {
345
if (o.type === 'string[]') {
346
if (!Array.isArray(val)) {
347
val = [val];
348
}
349
if (!o.allowEmptyValue) {
350
const sanitized = val.filter((v: string) => v.length > 0);
351
if (sanitized.length !== val.length) {
352
errorReporter.onEmptyValue(optionId);
353
val = sanitized.length > 0 ? sanitized : undefined;
354
}
355
}
356
} else if (o.type === 'string') {
357
if (Array.isArray(val)) {
358
val = val.pop(); // take the last
359
errorReporter.onMultipleValues(optionId, val);
360
} else if (!val && !o.allowEmptyValue) {
361
errorReporter.onEmptyValue(optionId);
362
val = undefined;
363
}
364
}
365
cleanedArgs[optionId] = val;
366
367
if (o.deprecationMessage) {
368
errorReporter.onDeprecatedOption(optionId, o.deprecationMessage);
369
}
370
}
371
delete remainingArgs[optionId];
372
}
373
374
for (const key in remainingArgs) {
375
errorReporter.onUnknownOption(key);
376
}
377
378
return cleanedArgs;
379
}
380
381
function formatUsage(optionId: string, option: Option<any>) {
382
let args = '';
383
if (option.args) {
384
if (Array.isArray(option.args)) {
385
args = ` <${option.args.join('> <')}>`;
386
} else {
387
args = ` <${option.args}>`;
388
}
389
}
390
if (option.alias) {
391
return `-${option.alias} --${optionId}${args}`;
392
}
393
return `--${optionId}${args}`;
394
}
395
396
// exported only for testing
397
export function formatOptions(options: OptionDescriptions<any>, columns: number): string[] {
398
const usageTexts: [string, string][] = [];
399
for (const optionId in options) {
400
const o = options[optionId];
401
const usageText = formatUsage(optionId, o);
402
usageTexts.push([usageText, o.description!]);
403
}
404
return formatUsageTexts(usageTexts, columns);
405
}
406
407
function formatUsageTexts(usageTexts: [string, string][], columns: number) {
408
const maxLength = usageTexts.reduce((previous, e) => Math.max(previous, e[0].length), 12);
409
const argLength = maxLength + 2/*left padding*/ + 1/*right padding*/;
410
if (columns - argLength < 25) {
411
// Use a condensed version on narrow terminals
412
return usageTexts.reduce<string[]>((r, ut) => r.concat([` ${ut[0]}`, ` ${ut[1]}`]), []);
413
}
414
const descriptionColumns = columns - argLength - 1;
415
const result: string[] = [];
416
for (const ut of usageTexts) {
417
const usage = ut[0];
418
const wrappedDescription = wrapText(ut[1], descriptionColumns);
419
const keyPadding = indent(argLength - usage.length - 2/*left padding*/);
420
result.push(' ' + usage + keyPadding + wrappedDescription[0]);
421
for (let i = 1; i < wrappedDescription.length; i++) {
422
result.push(indent(argLength) + wrappedDescription[i]);
423
}
424
}
425
return result;
426
}
427
428
function indent(count: number): string {
429
return ' '.repeat(count);
430
}
431
432
function wrapText(text: string, columns: number): string[] {
433
const lines: string[] = [];
434
while (text.length) {
435
let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
436
if (index === 0) {
437
index = columns;
438
}
439
const line = text.slice(0, index).trim();
440
text = text.slice(index).trimStart();
441
lines.push(line);
442
}
443
return lines;
444
}
445
446
export function buildHelpMessage(productName: string, executableName: string, version: string, options: OptionDescriptions<any>, capabilities?: { noPipe?: boolean; noInputFiles?: boolean; isChat?: boolean }): string {
447
const columns = (process.stdout).isTTY && (process.stdout).columns || 80;
448
const inputFiles = capabilities?.noInputFiles ? '' : capabilities?.isChat ? ` [${localize('cliPrompt', 'prompt')}]` : ` [${localize('paths', 'paths')}...]`;
449
const subcommand = capabilities?.isChat ? ' chat' : '';
450
451
const help = [`${productName} ${version}`];
452
help.push('');
453
help.push(`${localize('usage', "Usage")}: ${executableName}${subcommand} [${localize('options', "options")}]${inputFiles}`);
454
help.push('');
455
if (capabilities?.noPipe !== true) {
456
help.push(buildStdinMessage(executableName, capabilities?.isChat));
457
help.push('');
458
}
459
const optionsByCategory: { [P in keyof typeof helpCategories]?: OptionDescriptions<any> } = {};
460
const subcommands: { command: string; description: string }[] = [];
461
for (const optionId in options) {
462
const o = options[optionId];
463
if (o.type === 'subcommand') {
464
if (o.description) {
465
subcommands.push({ command: optionId, description: o.description });
466
}
467
} else if (o.description && o.cat) {
468
let optionsByCat = optionsByCategory[o.cat];
469
if (!optionsByCat) {
470
optionsByCategory[o.cat] = optionsByCat = {};
471
}
472
optionsByCat[optionId] = o;
473
}
474
}
475
476
for (const helpCategoryKey in optionsByCategory) {
477
const key = <keyof typeof helpCategories>helpCategoryKey;
478
479
const categoryOptions = optionsByCategory[key];
480
if (categoryOptions) {
481
help.push(helpCategories[key]);
482
help.push(...formatOptions(categoryOptions, columns));
483
help.push('');
484
}
485
}
486
487
if (subcommands.length) {
488
help.push(localize('subcommands', "Subcommands"));
489
help.push(...formatUsageTexts(subcommands.map(s => [s.command, s.description]), columns));
490
help.push('');
491
}
492
493
return help.join('\n');
494
}
495
496
export function buildStdinMessage(executableName: string, isChat?: boolean): string {
497
let example: string;
498
if (isWindows) {
499
if (isChat) {
500
example = `echo Hello World | ${executableName} chat <prompt> -`;
501
} else {
502
example = `echo Hello World | ${executableName} -`;
503
}
504
} else {
505
if (isChat) {
506
example = `ps aux | grep code | ${executableName} chat <prompt> -`;
507
} else {
508
example = `ps aux | grep code | ${executableName} -`;
509
}
510
}
511
512
return localize('stdinUsage', "To read from stdin, append '-' (e.g. '{0}')", example);
513
}
514
515
export function buildVersionMessage(version: string | undefined, commit: string | undefined): string {
516
return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`;
517
}
518
519