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

import * as minimist from '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

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

M
Martin Aeschlimann 已提交
23 24
export interface Option {
	id: string;
P
Peng Lyu 已提交
25
	type: 'boolean' | 'string';
M
Martin Aeschlimann 已提交
26
	alias?: string;
27
	deprecates?: string; // old deprecated id
M
Martin Aeschlimann 已提交
28 29 30 31
	args?: string | string[];
	description?: string;
	cat?: keyof HelpCategories;
}
J
Joao Moreno 已提交
32

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.") },
M
Martin Aeschlimann 已提交
45 46 47
	{ 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)") },

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.") },
S
Sandeep Somavarapu 已提交
51
	{ 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.") },
M
Martin Aeschlimann 已提交
52 53 54 55 56 57 58
	{ 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.") },

	{ 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") },
59
	{ id: 'disable-extensions', type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
M
Martin Aeschlimann 已提交
60 61
	{ id: 'disable-extension', type: 'string', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },

62 63
	{ 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 已提交
64
	{ id: 'disable-gpu', type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
P
Peng Lyu 已提交
65
	{ id: 'max-memory', type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
M
Martin Aeschlimann 已提交
66 67

	{ id: 'remote', type: 'string' },
68
	{ id: 'locate-extension', type: 'string' },
M
Martin Aeschlimann 已提交
69 70 71
	{ id: 'extensionDevelopmentPath', type: 'string' },
	{ id: 'extensionTestsPath', type: 'string' },
	{ id: 'debugId', type: 'string' },
72
	{ id: 'inspect-search', type: 'string', deprecates: 'debugSearch' },
73
	{ id: 'inspect-brk-search', type: 'string', deprecates: 'debugBrkSearch' },
M
Martin Aeschlimann 已提交
74 75 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: 'nolazy', type: 'boolean' },
	{ id: 'issue', 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
	{ id: 'prof-code-loading', type: 'boolean' },
97
	{ id: '_', type: 'string' }
M
Martin Aeschlimann 已提交
98 99 100 101 102 103 104
];

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) {
105 106 107 108
		if (isOptionSupported(o)) {
			if (o.alias) {
				alias[o.id] = o.alias;
			}
M
Martin Aeschlimann 已提交
109
		}
110

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

M
Martin Aeschlimann 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149
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 已提交
150 151
}

M
Martin Aeschlimann 已提交
152 153 154 155
// 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 已提交
156 157
	if (columns - argLength < 25) {
		// Use a condensed version on narrow terminals
M
Martin Aeschlimann 已提交
158
		return docOptions.reduce<string[]>((r, o, i) => r.concat([`  ${usageTexts[i]}`, `      ${o.description}`]), []);
D
Daniel Imms 已提交
159 160
	}
	let descriptionColumns = columns - argLength - 1;
M
Martin Aeschlimann 已提交
161
	let result: string[] = [];
M
Martin Aeschlimann 已提交
162 163 164 165 166
	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]);
167
		for (let i = 1; i < wrappedDescription.length; i++) {
M
Martin Aeschlimann 已提交
168
			result.push(indent(argLength) + wrappedDescription[i]);
D
Daniel Imms 已提交
169 170 171
		}
	});
	return result;
J
Joao Moreno 已提交
172 173
}

M
Martin Aeschlimann 已提交
174 175 176 177
function indent(count: number): string {
	return (<any>' ').repeat(count);
}

J
Johannes Rieken 已提交
178
function wrapText(text: string, columns: number): string[] {
M
Martin Aeschlimann 已提交
179
	let lines: string[] = [];
D
Daniel Imms 已提交
180 181
	while (text.length) {
		let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
D
Daniel Imms 已提交
182
		let line = text.slice(0, index).trim();
D
Daniel Imms 已提交
183 184 185 186 187 188
		text = text.slice(index);
		lines.push(line);
	}
	return lines;
}

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

M
Martin Aeschlimann 已提交
192
	let categories = new HelpCategories();
193

M
Martin Aeschlimann 已提交
194 195 196 197
	let help = [`${productName} ${version}`];
	help.push('');
	help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`);
	help.push('');
198 199 200 201 202 203 204
	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 已提交
205
	}
206
	for (const key in categories) {
M
Martin Aeschlimann 已提交
207 208
		let categoryOptions = options.filter(o => !!o.description && o.cat === key && isOptionSupported(o));
		if (categoryOptions.length) {
209
			help.push(categories[key as keyof HelpCategories]);
M
Martin Aeschlimann 已提交
210 211 212 213 214 215
			help.push(...formatOptions(categoryOptions, columns));
			help.push('');
		}
	}
	return help.join('\n');
}
B
Benjamin Pasero 已提交
216

M
Martin Aeschlimann 已提交
217 218
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}`;
219
}
M
Martin Aeschlimann 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

/**
 * Converts an argument into an array
 * @param arg a argument value. Can be undefined, an entry or an array
 */
export function asArray(arg: string | string[] | undefined): string[] {
	if (arg) {
		if (Array.isArray(arg)) {
			return arg;
		}
		return [arg];
	}
	return [];
}

/**
 * Returns whether an argument is present.
 */
export function hasArgs(arg: string | string[] | undefined): boolean {
	if (arg) {
		if (Array.isArray(arg)) {
			return !!arg.length;
		}
		return true;
	}
	return false;
246
}
B
Benjamin Pasero 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259

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 已提交
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
}

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;
	}
277
}