argv.ts 12.5 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import * as minimist from 'vscode-minimist';
M
Martin Aeschlimann 已提交
7
import * as os from 'os';
J
Joao Moreno 已提交
8
import { localize } from 'vs/nls';
M
Martin Aeschlimann 已提交
9
import { ParsedArgs } from 'vs/platform/environment/common/environment';
B
Benjamin Pasero 已提交
10
import { join } from 'vs/base/common/path';
L
Logan Ramos 已提交
11
import { writeFileSync } from 'vs/base/node/pfs';
J
Joao Moreno 已提交
12

J
Joao Moreno 已提交
13
/**
M
Martin Aeschlimann 已提交
14
 * This code is also used by standalone cli's. Avoid adding any other dependencies.
J
Joao Moreno 已提交
15
 */
J
Joao Moreno 已提交
16

17 18 19 20 21
const helpCategories = {
	o: localize('optionsUpperCase', "Options"),
	e: localize('extensionsManagement', "Extensions Management"),
	t: localize('troubleshooting', "Troubleshooting")
};
J
Joao Moreno 已提交
22

M
Martin Aeschlimann 已提交
23
export interface Option {
24
	id: keyof ParsedArgs;
25
	type: 'boolean' | 'string' | 'string[]';
M
Martin Aeschlimann 已提交
26
	alias?: string;
27
	deprecates?: string; // old deprecated id
M
Martin Aeschlimann 已提交
28 29
	args?: string | string[];
	description?: string;
30
	cat?: keyof typeof helpCategories;
M
Martin Aeschlimann 已提交
31
}
32
//_urls
M
Martin Aeschlimann 已提交
33 34 35 36 37 38 39 40 41 42 43
export const options: Option[] = [
	{ id: 'diff', type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") },
	{ id: 'add', type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") },
	{ id: '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.") },
	{ id: 'new-window', type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") },
	{ id: 'reuse-window', type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") },
	{ id: 'wait', type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") },
	{ id: 'locale', type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") },
	{ id: '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.") },
	{ id: 'version', type: 'boolean', cat: 'o', alias: 'v', description: localize('version', "Print version.") },
	{ id: 'help', type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
L
Logan Ramos 已提交
44
	{ id: 'telemetry', type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
45 46
	{ id: 'folder-uri', type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
	{ id: 'file-uri', type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
M
Martin Aeschlimann 已提交
47

48
	{ id: 'extensions-dir', type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
M
Martin Aeschlimann 已提交
49 50
	{ id: 'list-extensions', type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
	{ id: 'show-versions', type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
51
	{ id: 'category', type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") },
52 53 54
	{ id: 'install-extension', type: 'string[]', cat: 'e', args: 'extension-id | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") },
	{ id: 'uninstall-extension', type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") },
	{ id: 'enable-proposed-api', type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") },
M
Martin Aeschlimann 已提交
55 56 57 58 59

	{ id: 'verbose', type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") },
	{ id: 'log', type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") },
	{ id: 'status', type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
	{ id: 'prof-startup', type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") },
60
	{ id: 'disable-extensions', type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
61
	{ id: 'disable-extension', type: 'string[]', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },
M
Martin Aeschlimann 已提交
62

63 64
	{ id: 'inspect-extensions', type: 'string', deprecates: 'debugPluginHost', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") },
	{ id: 'inspect-brk-extensions', type: 'string', 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.") },
M
Martin Aeschlimann 已提交
65
	{ id: 'disable-gpu', type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
P
Peng Lyu 已提交
66
	{ id: 'max-memory', type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
M
Martin Aeschlimann 已提交
67 68

	{ id: 'remote', type: 'string' },
69 70
	{ id: 'locate-extension', type: 'string[]' },
	{ id: 'extensionDevelopmentPath', type: 'string[]' },
M
Martin Aeschlimann 已提交
71
	{ id: 'extensionTestsPath', type: 'string' },
72
	{ id: 'extension-development-confirm-save', type: 'boolean' },
M
Martin Aeschlimann 已提交
73
	{ id: 'debugId', type: 'string' },
74
	{ id: 'inspect-search', type: 'string', deprecates: 'debugSearch' },
75
	{ id: 'inspect-brk-search', type: 'string', deprecates: 'debugBrkSearch' },
M
Martin Aeschlimann 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	{ id: 'export-default-configuration', type: 'string' },
	{ id: 'install-source', type: 'string' },
	{ id: 'driver', type: 'string' },
	{ id: 'logExtensionHostCommunication', type: 'boolean' },
	{ id: 'skip-getting-started', type: 'boolean' },
	{ id: 'skip-release-notes', type: 'boolean' },
	{ id: 'sticky-quickopen', type: 'boolean' },
	{ id: 'disable-restore-windows', type: 'boolean' },
	{ id: 'disable-telemetry', type: 'boolean' },
	{ id: 'disable-updates', type: 'boolean' },
	{ id: 'disable-crash-reporter', type: 'boolean' },
	{ id: 'skip-add-to-recently-opened', type: 'boolean' },
	{ id: 'unity-launch', type: 'boolean' },
	{ id: 'open-url', type: 'boolean' },
	{ id: 'file-write', type: 'boolean' },
	{ id: 'file-chmod', type: 'boolean' },
	{ id: 'driver-verbose', type: 'boolean' },
	{ id: 'force', type: 'boolean' },
	{ id: 'trace-category-filter', type: 'string' },
	{ id: 'trace-options', type: 'string' },
96 97 98 99
	{ id: '_', type: 'string' },

	{ id: 'js-flags', type: 'string' }, // chrome js flags
	{ id: 'nolazy', type: 'boolean' }, // node inspect
M
Martin Aeschlimann 已提交
100 101 102 103 104 105 106
];

export function parseArgs(args: string[], isOptionSupported = (_: Option) => true): ParsedArgs {
	const alias: { [key: string]: string } = {};
	const string: string[] = [];
	const boolean: string[] = [];
	for (let o of options) {
107 108 109 110
		if (isOptionSupported(o)) {
			if (o.alias) {
				alias[o.id] = o.alias;
			}
M
Martin Aeschlimann 已提交
111
		}
112

113
		if (o.type === 'string' || o.type === 'string[]') {
M
Martin Aeschlimann 已提交
114
			string.push(o.id);
115 116 117
			if (o.deprecates) {
				string.push(o.deprecates);
			}
M
Martin Aeschlimann 已提交
118 119
		} else if (o.type === 'boolean') {
			boolean.push(o.id);
120 121 122
			if (o.deprecates) {
				boolean.push(o.deprecates);
			}
M
Martin Aeschlimann 已提交
123
		}
J
Joao Moreno 已提交
124
	}
M
Martin Aeschlimann 已提交
125
	// remote aliases to avoid confusion
126 127
	const parsedArgs = minimist(args, { string, boolean, alias });
	for (const o of options) {
M
Martin Aeschlimann 已提交
128 129 130
		if (o.alias) {
			delete parsedArgs[o.alias];
		}
131
		if (o.deprecates && parsedArgs.hasOwnProperty(o.deprecates) && !parsedArgs[o.id]) {
132 133 134
			parsedArgs[o.id] = parsedArgs[o.deprecates];
			delete parsedArgs[o.deprecates];
		}
135 136 137 138 139 140
		if (o.type === 'string[]') {
			const val = parsedArgs[o.id];
			if (val && !Array.isArray(val)) {
				parsedArgs[o.id] = [val];
			}
		}
M
Martin Aeschlimann 已提交
141
	}
J
Joao Moreno 已提交
142

143 144
	// https://github.com/microsoft/vscode/issues/58177
	parsedArgs._ = parsedArgs._.filter(arg => arg.length > 0);
145

M
Martin Aeschlimann 已提交
146
	return parsedArgs;
J
Joao Moreno 已提交
147 148
}

M
Martin Aeschlimann 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161
function formatUsage(option: Option) {
	let args = '';
	if (option.args) {
		if (Array.isArray(option.args)) {
			args = ` <${option.args.join('> <')}>`;
		} else {
			args = ` <${option.args}>`;
		}
	}
	if (option.alias) {
		return `-${option.alias} --${option.id}${args}`;
	}
	return `--${option.id}${args}`;
J
Joao Moreno 已提交
162 163
}

M
Martin Aeschlimann 已提交
164 165 166 167
// exported only for testing
export function formatOptions(docOptions: Option[], columns: number): string[] {
	let usageTexts = docOptions.map(formatUsage);
	let argLength = Math.max.apply(null, usageTexts.map(k => k.length)) + 2/*left padding*/ + 1/*right padding*/;
D
Daniel Imms 已提交
168 169
	if (columns - argLength < 25) {
		// Use a condensed version on narrow terminals
M
Martin Aeschlimann 已提交
170
		return docOptions.reduce<string[]>((r, o, i) => r.concat([`  ${usageTexts[i]}`, `      ${o.description}`]), []);
D
Daniel Imms 已提交
171 172
	}
	let descriptionColumns = columns - argLength - 1;
M
Martin Aeschlimann 已提交
173
	let result: string[] = [];
M
Martin Aeschlimann 已提交
174 175 176 177 178
	docOptions.forEach((o, i) => {
		let usage = usageTexts[i];
		let wrappedDescription = wrapText(o.description!, descriptionColumns);
		let keyPadding = indent(argLength - usage.length - 2/*left padding*/);
		result.push('  ' + usage + keyPadding + wrappedDescription[0]);
179
		for (let i = 1; i < wrappedDescription.length; i++) {
M
Martin Aeschlimann 已提交
180
			result.push(indent(argLength) + wrappedDescription[i]);
D
Daniel Imms 已提交
181 182 183
		}
	});
	return result;
J
Joao Moreno 已提交
184 185
}

M
Martin Aeschlimann 已提交
186 187 188 189
function indent(count: number): string {
	return (<any>' ').repeat(count);
}

J
Johannes Rieken 已提交
190
function wrapText(text: string, columns: number): string[] {
M
Martin Aeschlimann 已提交
191
	let lines: string[] = [];
D
Daniel Imms 已提交
192 193
	while (text.length) {
		let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
D
Daniel Imms 已提交
194
		let line = text.slice(0, index).trim();
D
Daniel Imms 已提交
195 196 197 198 199 200
		text = text.slice(index);
		lines.push(line);
	}
	return lines;
}

M
Martin Aeschlimann 已提交
201
export function buildHelpMessage(productName: string, executableName: string, version: string, isOptionSupported = (_: Option) => true, isPipeSupported = true): string {
M
Martin Aeschlimann 已提交
202
	const columns = (process.stdout).isTTY && (process.stdout).columns || 80;
J
Joao Moreno 已提交
203

M
Martin Aeschlimann 已提交
204 205 206 207
	let help = [`${productName} ${version}`];
	help.push('');
	help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`);
	help.push('');
208 209 210 211 212 213 214
	if (isPipeSupported) {
		if (os.platform() === 'win32') {
			help.push(localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", executableName));
		} else {
			help.push(localize('stdinUnix', "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", executableName));
		}
		help.push('');
M
Martin Aeschlimann 已提交
215
	}
216 217 218
	for (let helpCategoryKey in helpCategories) {
		const key = <keyof typeof helpCategories>helpCategoryKey;

M
Martin Aeschlimann 已提交
219 220
		let categoryOptions = options.filter(o => !!o.description && o.cat === key && isOptionSupported(o));
		if (categoryOptions.length) {
221
			help.push(helpCategories[key]);
M
Martin Aeschlimann 已提交
222 223 224 225 226 227
			help.push(...formatOptions(categoryOptions, columns));
			help.push('');
		}
	}
	return help.join('\n');
}
B
Benjamin Pasero 已提交
228

M
Martin Aeschlimann 已提交
229 230
export function buildVersionMessage(version: string | undefined, commit: string | undefined): string {
	return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`;
231
}
M
Martin Aeschlimann 已提交
232

B
Benjamin Pasero 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245

export function addArg(argv: string[], ...args: string[]): string[] {
	const endOfArgsMarkerIndex = argv.indexOf('--');
	if (endOfArgsMarkerIndex === -1) {
		argv.push(...args);
	} else {
		// if the we have an argument "--" (end of argument marker)
		// we cannot add arguments at the end. rather, we add
		// arguments before the "--" marker.
		argv.splice(endOfArgsMarkerIndex, 0, ...args);
	}

	return argv;
M
Martin Aeschlimann 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
}

export function createWaitMarkerFile(verbose?: boolean): string | undefined {
	const randomWaitMarkerPath = join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10));

	try {
		writeFileSync(randomWaitMarkerPath, '');
		if (verbose) {
			console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`);
		}
		return randomWaitMarkerPath;
	} catch (err) {
		if (verbose) {
			console.error(`Failed to create marker file for --wait: ${err}`);
		}
		return undefined;
	}
263
}