argv.ts 11.3 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';
J
Joao Moreno 已提交
10

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

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

M
Martin Aeschlimann 已提交
21 22 23 24 25 26 27 28
export interface Option {
	id: string;
	type: 'boolean' | 'string';
	alias?: string;
	args?: string | string[];
	description?: string;
	cat?: keyof HelpCategories;
}
J
Joao Moreno 已提交
29

M
Martin Aeschlimann 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42
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.") },
	{ 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 已提交
43
	{ id: 'workspace-uri', type: 'string', cat: 'o', args: 'uri', description: localize('workspaceUri', "Opens a window with given workspace file") },
M
Martin Aeschlimann 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

	{ id: 'extensions-dir', type: 'string', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
	{ 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.") },
	{ id: 'install-extension', type: 'string', cat: 'e', args: 'extension-id', 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.") },

	{ 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-modules', type: 'boolean', alias: 'p', cat: 't', description: localize('prof-modules', "Capture performance markers while loading JS modules and print them with 'F1 > Developer: Startup Performance") },
	{ id: 'prof-startup', type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") },
	{ id: 'disable-extensions', type: 'boolean', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
	{ id: 'disable-extension', type: 'string', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },

	{ id: 'inspect-extensions', type: 'string', 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-search', type: 'string', 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.") },
	{ id: 'disable-gpu', type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
	{ id: 'upload-logs', type: 'string', cat: 't', description: localize('uploadLogs', "Uploads logs from current session to a secure endpoint.") },
	{ id: 'max-memory', type: 'boolean', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },

	{ id: 'remote', type: 'string' },
	{ id: 'extensionDevelopmentPath', type: 'string' },
	{ id: 'extensionTestsPath', type: 'string' },
	{ id: 'debugId', type: 'string' },
	{ id: 'inspect-search', type: 'string' },
	{ id: 'inspect-brk-extensions', type: 'string' },
	{ 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' },
	{ id: 'prof-code-loading', type: 'boolean' },

	{ id: 'debugPluginHost', type: 'string', alias: 'inspect-extensions' },
	{ id: 'debugBrkPluginHost', type: 'string', alias: 'inspect-brk-extensions' }
];

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) {
		if (o.alias && isOptionSupported(o)) {
			alias[o.id] = o.alias;
		}
		if (o.type === 'string') {
			string.push(o.id);
		} else if (o.type === 'boolean') {
			boolean.push(o.id);
		}
J
Joao Moreno 已提交
113
	}
M
Martin Aeschlimann 已提交
114 115 116 117 118 119 120 121
	// remote aliases to avoid confusion
	const parsedArgs = minimist(args, { string, boolean, alias }) as ParsedArgs;
	for (let o of options) {
		if (o.alias) {
			delete parsedArgs[o.alias];
		}
	}
	return parsedArgs;
J
Joao Moreno 已提交
122 123
}

M
Martin Aeschlimann 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136
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 已提交
137 138
}

M
Martin Aeschlimann 已提交
139 140 141 142
// 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 已提交
143 144
	if (columns - argLength < 25) {
		// Use a condensed version on narrow terminals
M
Martin Aeschlimann 已提交
145
		return docOptions.reduce<string[]>((r, o, i) => r.concat([`  ${usageTexts[i]}`, `      ${o.description}`]), []);
D
Daniel Imms 已提交
146 147
	}
	let descriptionColumns = columns - argLength - 1;
M
Martin Aeschlimann 已提交
148
	let result: string[] = [];
M
Martin Aeschlimann 已提交
149 150 151 152 153
	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]);
154
		for (let i = 1; i < wrappedDescription.length; i++) {
M
Martin Aeschlimann 已提交
155
			result.push(indent(argLength) + wrappedDescription[i]);
D
Daniel Imms 已提交
156 157 158
		}
	});
	return result;
J
Joao Moreno 已提交
159 160
}

M
Martin Aeschlimann 已提交
161 162 163 164
function indent(count: number): string {
	return (<any>' ').repeat(count);
}

J
Johannes Rieken 已提交
165
function wrapText(text: string, columns: number): string[] {
M
Martin Aeschlimann 已提交
166
	let lines: string[] = [];
D
Daniel Imms 已提交
167 168
	while (text.length) {
		let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
D
Daniel Imms 已提交
169
		let line = text.slice(0, index).trim();
D
Daniel Imms 已提交
170 171 172 173 174 175
		text = text.slice(index);
		lines.push(line);
	}
	return lines;
}

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

M
Martin Aeschlimann 已提交
179
	let categories = new HelpCategories();
180

M
Martin Aeschlimann 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	let help = [`${productName} ${version}`];
	help.push('');
	help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`);
	help.push('');
	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('');
	for (let key in categories) {
		let categoryOptions = options.filter(o => !!o.description && o.cat === key && isOptionSupported(o));
		if (categoryOptions.length) {
			help.push(categories[key]);
			help.push(...formatOptions(categoryOptions, columns));
			help.push('');
		}
	}
	return help.join('\n');
}
B
Benjamin Pasero 已提交
201

M
Martin Aeschlimann 已提交
202 203
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}`;
204
}
M
Martin Aeschlimann 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

/**
 * 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;
231
}