Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* A list of command line arguments we support natively.
*/
export interface NativeParsedArgs {
_: string[];
'folder-uri'?: string[]; // undefined or array of 1 or more
'file-uri'?: string[]; // undefined or array of 1 or more
_urls?: string[];
help?: boolean;
version?: boolean;
telemetry?: boolean;
status?: boolean;
wait?: boolean;
waitMarkerFilePath?: string;
diff?: boolean;
add?: boolean;
goto?: boolean;
'new-window'?: boolean;
'unity-launch'?: boolean; // Always open a new window, except if opening the first window or opening a file or folder as part of the launch.
'reuse-window'?: boolean;
locale?: string;
'user-data-dir'?: string;
'prof-startup'?: boolean;
'prof-startup-prefix'?: string;
'prof-append-timers'?: string;
verbose?: boolean;
trace?: boolean;
'trace-category-filter'?: string;
'trace-options'?: string;
'open-devtools'?: boolean;
log?: string;
logExtensionHostCommunication?: boolean;
'extensions-dir'?: string;
'extensions-download-dir'?: string;
'builtin-extensions-dir'?: string;
extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs
extensionTestsPath?: string; // either a local path or a URI
'inspect-extensions'?: string;
'inspect-brk-extensions'?: string;
debugId?: string;
debugRenderer?: boolean; // whether we expect a debugger (js-debug) to attach to the renderer, incl webviews+webworker
'inspect-search'?: string;
'inspect-brk-search'?: string;
'disable-extensions'?: boolean;
'disable-extension'?: string[]; // undefined or array of 1 or more
'list-extensions'?: boolean;
'show-versions'?: boolean;
'category'?: string;
'install-extension'?: string[]; // undefined or array of 1 or more
'install-builtin-extension'?: string[]; // undefined or array of 1 or more
'uninstall-extension'?: string[]; // undefined or array of 1 or more
'locate-extension'?: string[]; // undefined or array of 1 or more
'enable-proposed-api'?: string[]; // undefined or array of 1 or more
'open-url'?: boolean;
'skip-release-notes'?: boolean;
'disable-telemetry'?: boolean;
'export-default-configuration'?: string;
'install-source'?: string;
'disable-updates'?: boolean;
'disable-crash-reporter'?: boolean;
'crash-reporter-directory'?: string;
'crash-reporter-id'?: string;
'skip-add-to-recently-opened'?: boolean;
'max-memory'?: string;
'file-write'?: boolean;
'file-chmod'?: boolean;
'driver'?: string;
'driver-verbose'?: boolean;
'remote'?: string;
'disable-user-env-probe'?: boolean;
'force'?: boolean;
'do-not-sync'?: boolean;
'force-user-env'?: boolean;
'sync'?: 'on' | 'off';
'__sandbox'?: boolean;
// chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches
'no-proxy-server'?: boolean;
'proxy-server'?: string;
'proxy-bypass-list'?: string;
'proxy-pac-url'?: string;
'inspect'?: string;
'inspect-brk'?: string;
'js-flags'?: string;
'disable-gpu'?: boolean;
'nolazy'?: boolean;
'force-device-scale-factor'?: string;
'force-renderer-accessibility'?: boolean;
'ignore-certificate-errors'?: boolean;
'allow-insecure-localhost'?: boolean;
'log-net-log'?: string;
}

View File

@@ -0,0 +1,139 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
export const INativeEnvironmentService = createDecorator<INativeEnvironmentService>('nativeEnvironmentService');
export interface IDebugParams {
port: number | null;
break: boolean;
}
export interface IExtensionHostDebugParams extends IDebugParams {
debugId?: string;
}
/**
* A basic environment service that can be used in various processes,
* such as main, renderer and shared process. Use subclasses of this
* service for specific environment.
*/
export interface IEnvironmentService {
readonly _serviceBrand: undefined;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
//
// AS SUCH:
// - PUT NON-WEB PROPERTIES INTO NATIVE ENVIRONMENT SERVICE
// - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
// - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// --- user roaming data
userRoamingDataHome: URI;
settingsResource: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI;
argvResource: URI;
snippetsHome: URI;
// --- data paths
untitledWorkspacesHome: URI;
globalStorageHome: URI;
workspaceStorageHome: URI;
// --- settings sync
userDataSyncHome: URI;
userDataSyncLogResource: URI;
sync: 'on' | 'off' | undefined;
// --- extension development
debugExtensionHost: IExtensionHostDebugParams;
isExtensionDevelopment: boolean;
disableExtensions: boolean | string[];
extensionDevelopmentLocationURI?: URI[];
extensionTestsLocationURI?: URI;
// --- logging
logsPath: string;
logLevel?: string;
verbose: boolean;
isBuilt: boolean;
// --- telemetry
disableTelemetry: boolean;
telemetryLogResource: URI;
serviceMachineIdResource: URI;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
//
// AS SUCH:
// - PUT NON-WEB PROPERTIES INTO NATIVE ENVIRONMENT SERVICE
// - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
// - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
/**
* A subclass of the `IEnvironmentService` to be used only in native
* environments (Windows, Linux, macOS) but not e.g. web.
*/
export interface INativeEnvironmentService extends IEnvironmentService {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
//
// AS SUCH:
// - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
// - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// --- CLI Arguments
args: NativeParsedArgs;
// --- paths
appRoot: string;
userHome: URI;
appSettingsHome: URI;
tmpDir: URI;
userDataPath: string;
machineSettingsResource: URI;
installSourcePath: string;
// --- IPC Handles
sharedIPCHandle: string;
// --- Extensions
extensionsPath?: string;
extensionsDownloadPath: string;
builtinExtensionsPath: string;
// --- Smoke test support
driverHandle?: string;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
//
// AS SUCH:
// - PUT NON-WEB PROPERTIES INTO NATIVE ENVIRONMENT SERVICE
// - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
// - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

View File

@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/path';
import { memoize } from 'vs/base/common/decorators';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { createStaticIPCHandle } from 'vs/base/parts/ipc/node/ipc.net';
import product from 'vs/platform/product/common/product';
export const IEnvironmentMainService = createDecorator<IEnvironmentMainService>('nativeEnvironmentService');
/**
* A subclass of the `INativeEnvironmentService` to be used only in electron-main
* environments.
*/
export interface IEnvironmentMainService extends INativeEnvironmentService {
// --- backup paths
backupHome: string;
backupWorkspacesPath: string;
// --- V8 script cache path
nodeCachedDataDir?: string;
// --- IPC
mainIPCHandle: string;
// --- config
sandbox: boolean;
driverVerbose: boolean;
disableUpdates: boolean;
}
export class EnvironmentMainService extends NativeEnvironmentService {
@memoize
get backupHome(): string { return join(this.userDataPath, 'Backups'); }
@memoize
get backupWorkspacesPath(): string { return join(this.backupHome, 'workspaces.json'); }
@memoize
get mainIPCHandle(): string { return createStaticIPCHandle(this.userDataPath, 'main', product.version); }
@memoize
get sandbox(): boolean { return !!this._args['__sandbox']; }
@memoize
get driverVerbose(): boolean { return !!this._args['driver-verbose']; }
@memoize
get disableUpdates(): boolean { return !!this._args['disable-updates']; }
@memoize
get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; }
}

View File

@@ -0,0 +1,321 @@
/*---------------------------------------------------------------------------------------------
* 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';
import { localize } from 'vs/nls';
import { isWindows } from 'vs/base/common/platform';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
/**
* This code is also used by standalone cli's. Avoid adding any other dependencies.
*/
const helpCategories = {
o: localize('optionsUpperCase', "Options"),
e: localize('extensionsManagement', "Extensions Management"),
t: localize('troubleshooting', "Troubleshooting")
};
export interface Option<OptionType> {
type: OptionType;
alias?: string;
deprecates?: string; // old deprecated id
args?: string | string[];
description?: string;
cat?: keyof typeof helpCategories;
}
export type OptionDescriptions<T> = {
[P in keyof T]: Option<OptionTypeName<T[P]>>;
};
type OptionTypeName<T> =
T extends boolean ? 'boolean' :
T extends string ? 'string' :
T extends string[] ? 'string[]' :
T extends undefined ? 'undefined' :
'unknown';
export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") },
'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") },
'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.") },
'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") },
'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") },
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") },
'waitMarkerFilePath': { type: 'string' },
'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") },
'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.") },
'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
'extensions-download-dir': { type: 'string' },
'builtin-extensions-dir': { type: 'string' },
'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") },
'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts. The identifier of an extension is always `${publisher}.${name}`. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") },
'uninstall-extension': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") },
'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.") },
'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") },
'verbose': { type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") },
'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'.") },
'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") },
'prof-append-timers': { type: 'string' },
'prof-startup-prefix': { type: 'string' },
'disable-extensions': { type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
'disable-extension': { type: 'string[]', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },
'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off"), args: ['on', 'off'] },
'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.") },
'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.") },
'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") },
'max-memory': { type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
'remote': { type: 'string' },
'locate-extension': { type: 'string[]' },
'extensionDevelopmentPath': { type: 'string[]' },
'extensionTestsPath': { type: 'string' },
'debugId': { type: 'string' },
'debugRenderer': { type: 'boolean' },
'inspect-search': { type: 'string', deprecates: 'debugSearch' },
'inspect-brk-search': { type: 'string', deprecates: 'debugBrkSearch' },
'export-default-configuration': { type: 'string' },
'install-source': { type: 'string' },
'driver': { type: 'string' },
'logExtensionHostCommunication': { type: 'boolean' },
'skip-release-notes': { type: 'boolean' },
'disable-telemetry': { type: 'boolean' },
'disable-updates': { type: 'boolean' },
'disable-crash-reporter': { type: 'boolean' },
'crash-reporter-directory': { type: 'string' },
'crash-reporter-id': { type: 'string' },
'disable-user-env-probe': { type: 'boolean' },
'skip-add-to-recently-opened': { type: 'boolean' },
'unity-launch': { type: 'boolean' },
'open-url': { type: 'boolean' },
'file-write': { type: 'boolean' },
'file-chmod': { type: 'boolean' },
'driver-verbose': { type: 'boolean' },
'install-builtin-extension': { type: 'string[]' },
'force': { type: 'boolean' },
'do-not-sync': { type: 'boolean' },
'trace': { type: 'boolean' },
'trace-category-filter': { type: 'string' },
'trace-options': { type: 'string' },
'force-user-env': { type: 'boolean' },
'open-devtools': { type: 'boolean' },
'__sandbox': { type: 'boolean' },
// chromium flags
'no-proxy-server': { type: 'boolean' },
'proxy-server': { type: 'string' },
'proxy-bypass-list': { type: 'string' },
'proxy-pac-url': { type: 'string' },
'js-flags': { type: 'string' }, // chrome js flags
'inspect': { type: 'string' },
'inspect-brk': { type: 'string' },
'nolazy': { type: 'boolean' }, // node inspect
'force-device-scale-factor': { type: 'string' },
'force-renderer-accessibility': { type: 'boolean' },
'ignore-certificate-errors': { type: 'boolean' },
'allow-insecure-localhost': { type: 'boolean' },
'log-net-log': { type: 'string' },
'_urls': { type: 'string[]' },
_: { type: 'string[]' } // main arguments
};
export interface ErrorReporter {
onUnknownOption(id: string): void;
onMultipleValues(id: string, usedValue: string): void;
}
const ignoringReporter: ErrorReporter = {
onUnknownOption: () => { },
onMultipleValues: () => { }
};
export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, errorReporter: ErrorReporter = ignoringReporter): T {
const alias: { [key: string]: string } = {};
const string: string[] = [];
const boolean: string[] = [];
for (let optionId in options) {
if (optionId[0] === '_') {
continue;
}
const o = options[optionId];
if (o.alias) {
alias[optionId] = o.alias;
}
if (o.type === 'string' || o.type === 'string[]') {
string.push(optionId);
if (o.deprecates) {
string.push(o.deprecates);
}
} else if (o.type === 'boolean') {
boolean.push(optionId);
if (o.deprecates) {
boolean.push(o.deprecates);
}
}
}
// remove aliases to avoid confusion
const parsedArgs = minimist(args, { string, boolean, alias });
const cleanedArgs: any = {};
const remainingArgs: any = parsedArgs;
// https://github.com/microsoft/vscode/issues/58177, https://github.com/microsoft/vscode/issues/106617
cleanedArgs._ = parsedArgs._.map(arg => String(arg)).filter(arg => arg.length > 0);
delete remainingArgs._;
for (let optionId in options) {
const o = options[optionId];
if (o.alias) {
delete remainingArgs[o.alias];
}
let val = remainingArgs[optionId];
if (o.deprecates && remainingArgs.hasOwnProperty(o.deprecates)) {
if (!val) {
val = remainingArgs[o.deprecates];
}
delete remainingArgs[o.deprecates];
}
if (typeof val !== 'undefined') {
if (o.type === 'string[]') {
if (val && !Array.isArray(val)) {
val = [val];
}
} else if (o.type === 'string') {
if (Array.isArray(val)) {
val = val.pop(); // take the last
errorReporter.onMultipleValues(optionId, val);
}
}
cleanedArgs[optionId] = val;
}
delete remainingArgs[optionId];
}
for (let key in remainingArgs) {
errorReporter.onUnknownOption(key);
}
return cleanedArgs;
}
function formatUsage(optionId: string, option: Option<any>) {
let args = '';
if (option.args) {
if (Array.isArray(option.args)) {
args = ` <${option.args.join('> <')}>`;
} else {
args = ` <${option.args}>`;
}
}
if (option.alias) {
return `-${option.alias} --${optionId}${args}`;
}
return `--${optionId}${args}`;
}
// exported only for testing
export function formatOptions(options: OptionDescriptions<any>, columns: number): string[] {
let maxLength = 0;
let usageTexts: [string, string][] = [];
for (const optionId in options) {
const o = options[optionId];
const usageText = formatUsage(optionId, o);
maxLength = Math.max(maxLength, usageText.length);
usageTexts.push([usageText, o.description!]);
}
let argLength = maxLength + 2/*left padding*/ + 1/*right padding*/;
if (columns - argLength < 25) {
// Use a condensed version on narrow terminals
return usageTexts.reduce<string[]>((r, ut) => r.concat([` ${ut[0]}`, ` ${ut[1]}`]), []);
}
let descriptionColumns = columns - argLength - 1;
let result: string[] = [];
for (const ut of usageTexts) {
let usage = ut[0];
let wrappedDescription = wrapText(ut[1], descriptionColumns);
let keyPadding = indent(argLength - usage.length - 2/*left padding*/);
result.push(' ' + usage + keyPadding + wrappedDescription[0]);
for (let i = 1; i < wrappedDescription.length; i++) {
result.push(indent(argLength) + wrappedDescription[i]);
}
}
return result;
}
function indent(count: number): string {
return (<any>' ').repeat(count);
}
function wrapText(text: string, columns: number): string[] {
let lines: string[] = [];
while (text.length) {
let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
let line = text.slice(0, index).trim();
text = text.slice(index);
lines.push(line);
}
return lines;
}
export function buildHelpMessage(productName: string, executableName: string, version: string, options: OptionDescriptions<any>, isPipeSupported = true): string {
const columns = (process.stdout).isTTY && (process.stdout).columns || 80;
let help = [`${productName} ${version}`];
help.push('');
help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`);
help.push('');
if (isPipeSupported) {
if (isWindows) {
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('');
}
const optionsByCategory: { [P in keyof typeof helpCategories]?: OptionDescriptions<any> } = {};
for (const optionId in options) {
const o = options[optionId];
if (o.description && o.cat) {
let optionsByCat = optionsByCategory[o.cat];
if (!optionsByCat) {
optionsByCategory[o.cat] = optionsByCat = {};
}
optionsByCat[optionId] = o;
}
}
for (let helpCategoryKey in optionsByCategory) {
const key = <keyof typeof helpCategories>helpCategoryKey;
let categoryOptions = optionsByCategory[key];
if (categoryOptions) {
help.push(helpCategories[key]);
help.push(...formatOptions(categoryOptions, columns));
help.push('');
}
}
return help.join('\n');
}
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}`;
}

View File

@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { localize } from 'vs/nls';
import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files';
import { parseArgs, ErrorReporter, OPTIONS } from 'vs/platform/environment/node/argv';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): NativeParsedArgs {
const errorReporter: ErrorReporter = {
onUnknownOption: (id) => {
console.warn(localize('unknownOption', "Warning: '{0}' is not in the list of known options, but still passed to Electron/Chromium.", id));
},
onMultipleValues: (id, val) => {
console.warn(localize('multipleValues', "Option '{0}' is defined more than once. Using value '{1}.'", id, val));
}
};
const args = parseArgs(cmdLineArgs, OPTIONS, reportWarnings ? errorReporter : undefined);
if (args.goto) {
args._.forEach(arg => assert(/^(\w:)?[^:]+(:\d*){0,2}$/.test(arg), localize('gotoValidation', "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`.")));
}
if (args['max-memory']) {
assert(parseInt(args['max-memory']) >= MIN_MAX_MEMORY_SIZE_MB, `The max-memory argument cannot be specified lower than ${MIN_MAX_MEMORY_SIZE_MB} MB.`);
}
return args;
}
function stripAppPath(argv: string[]): string[] | undefined {
const index = argv.findIndex(a => !/^-/.test(a));
if (index > -1) {
return [...argv.slice(0, index), ...argv.slice(index + 1)];
}
return undefined;
}
/**
* Use this to parse raw code process.argv such as: `Electron . --verbose --wait`
*/
export function parseMainProcessArgv(processArgv: string[]): NativeParsedArgs {
let [, ...args] = processArgv;
// If dev, remove the first non-option argument: it's the app location
if (process.env['VSCODE_DEV']) {
args = stripAppPath(args) || [];
}
// If called from CLI, don't report warnings as they are already reported.
let reportWarnings = !process.env['VSCODE_CLI'];
return parseAndValidate(args, reportWarnings);
}
/**
* Use this to parse raw code CLI process.argv such as: `Electron cli.js . --verbose --wait`
*/
export function parseCLIProcessArgv(processArgv: string[]): NativeParsedArgs {
let [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location
return parseAndValidate(args, true);
}
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;
}

View File

@@ -0,0 +1,250 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import * as paths from 'vs/base/node/paths';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
import { memoize } from 'vs/base/common/decorators';
import product from 'vs/platform/product/common/product';
import { toLocalISOString } from 'vs/base/common/date';
import { FileAccess } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { createStaticIPCHandle } from 'vs/base/parts/ipc/node/ipc.net';
export class NativeEnvironmentService implements INativeEnvironmentService {
declare readonly _serviceBrand: undefined;
get args(): NativeParsedArgs { return this._args; }
@memoize
get appRoot(): string { return path.dirname(FileAccess.asFileUri('', require).fsPath); }
readonly logsPath: string;
@memoize
get userHome(): URI { return URI.file(os.homedir()); }
@memoize
get userDataPath(): string {
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
return path.join(vscodePortable, 'user-data');
}
return parseUserDataDir(this._args, process);
}
@memoize
get appSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'User')); }
@memoize
get tmpDir(): URI { return URI.file(os.tmpdir()); }
@memoize
get userRoamingDataHome(): URI { return this.appSettingsHome; }
@memoize
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
@memoize
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); }
@memoize
get userDataSyncLogResource(): URI { return URI.file(path.join(this.logsPath, 'userDataSync.log')); }
@memoize
get sync(): 'on' | 'off' | undefined { return this.args.sync; }
@memoize
get machineSettingsResource(): URI { return resources.joinPath(URI.file(path.join(this.userDataPath, 'Machine')), 'settings.json'); }
@memoize
get globalStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'globalStorage'); }
@memoize
get workspaceStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'workspaceStorage'); }
@memoize
get keybindingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keybindings.json'); }
@memoize
get keyboardLayoutResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); }
@memoize
get argvResource(): URI {
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
return URI.file(path.join(vscodePortable, 'argv.json'));
}
return resources.joinPath(this.userHome, product.dataFolderName, 'argv.json');
}
@memoize
get snippetsHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'snippets'); }
@memoize
get isExtensionDevelopment(): boolean { return !!this._args.extensionDevelopmentPath; }
@memoize
get untitledWorkspacesHome(): URI { return URI.file(path.join(this.userDataPath, 'Workspaces')); }
@memoize
get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); }
@memoize
get builtinExtensionsPath(): string {
const fromArgs = parsePathArg(this._args['builtin-extensions-dir'], process);
if (fromArgs) {
return fromArgs;
} else {
return path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions'));
}
}
get extensionsDownloadPath(): string {
const fromArgs = parsePathArg(this._args['extensions-download-dir'], process);
if (fromArgs) {
return fromArgs;
} else {
return path.join(this.userDataPath, 'CachedExtensionVSIXs');
}
}
@memoize
get extensionsPath(): string {
const fromArgs = parsePathArg(this._args['extensions-dir'], process);
if (fromArgs) {
return fromArgs;
}
const vscodeExtensions = process.env['VSCODE_EXTENSIONS'];
if (vscodeExtensions) {
return vscodeExtensions;
}
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
return path.join(vscodePortable, 'extensions');
}
return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath;
}
@memoize
get extensionDevelopmentLocationURI(): URI[] | undefined {
const s = this._args.extensionDevelopmentPath;
if (Array.isArray(s)) {
return s.map(p => {
if (/^[^:/?#]+?:\/\//.test(p)) {
return URI.parse(p);
}
return URI.file(path.normalize(p));
});
}
return undefined;
}
@memoize
get extensionTestsLocationURI(): URI | undefined {
const s = this._args.extensionTestsPath;
if (s) {
if (/^[^:/?#]+?:\/\//.test(s)) {
return URI.parse(s);
}
return URI.file(path.normalize(s));
}
return undefined;
}
get disableExtensions(): boolean | string[] {
if (this._args['disable-extensions']) {
return true;
}
const disableExtensions = this._args['disable-extension'];
if (disableExtensions) {
if (typeof disableExtensions === 'string') {
return [disableExtensions];
}
if (Array.isArray(disableExtensions) && disableExtensions.length > 0) {
return disableExtensions;
}
}
return false;
}
@memoize
get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); }
get debugRenderer(): boolean { return !!this._args.debugRenderer; }
get isBuilt(): boolean { return !process.env['VSCODE_DEV']; }
get verbose(): boolean { return !!this._args.verbose; }
get logLevel(): string | undefined { return this._args.log; }
@memoize
get sharedIPCHandle(): string { return createStaticIPCHandle(this.userDataPath, 'shared', product.version); }
@memoize
get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
get crashReporterId(): string | undefined { return this._args['crash-reporter-id']; }
get crashReporterDirectory(): string | undefined { return this._args['crash-reporter-directory']; }
get driverHandle(): string | undefined { return this._args['driver']; }
@memoize
get telemetryLogResource(): URI { return URI.file(path.join(this.logsPath, 'telemetry.log')); }
get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; }
constructor(protected _args: NativeParsedArgs) {
if (!process.env['VSCODE_LOGS']) {
const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '');
process.env['VSCODE_LOGS'] = path.join(this.userDataPath, 'logs', key);
}
this.logsPath = process.env['VSCODE_LOGS']!;
}
}
export function parseExtensionHostPort(args: NativeParsedArgs, isBuild: boolean): IExtensionHostDebugParams {
return parseDebugPort(args['inspect-extensions'], args['inspect-brk-extensions'], 5870, isBuild, args.debugId);
}
export function parseSearchPort(args: NativeParsedArgs, isBuild: boolean): IDebugParams {
return parseDebugPort(args['inspect-search'], args['inspect-brk-search'], 5876, isBuild);
}
function parseDebugPort(debugArg: string | undefined, debugBrkArg: string | undefined, defaultBuildPort: number, isBuild: boolean, debugId?: string): IExtensionHostDebugParams {
const portStr = debugBrkArg || debugArg;
const port = Number(portStr) || (!isBuild ? defaultBuildPort : null);
const brk = port ? Boolean(!!debugBrkArg) : false;
return { port, break: brk, debugId };
}
export function parsePathArg(arg: string | undefined, process: NodeJS.Process): string | undefined {
if (!arg) {
return undefined;
}
// Determine if the arg is relative or absolute, if relative use the original CWD
// (VSCODE_CWD), not the potentially overridden one (process.cwd()).
const resolved = path.resolve(arg);
if (path.normalize(arg) === resolved) {
return resolved;
}
return path.resolve(process.env['VSCODE_CWD'] || process.cwd(), arg);
}
export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string {
return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath(process.platform));
}

View File

@@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small.
*/
import * as paths from 'vs/base/common/path';
import * as fs from 'fs';
import * as os from 'os';
import { resolveTerminalEncoding } from 'vs/base/node/terminalEncoding';
export function hasStdinWithoutTty() {
try {
return !process.stdin.isTTY; // Via https://twitter.com/MylesBorins/status/782009479382626304
} catch (error) {
// Windows workaround for https://github.com/nodejs/node/issues/11656
}
return false;
}
export function stdinDataListener(durationinMs: number): Promise<boolean> {
return new Promise(resolve => {
const dataListener = () => resolve(true);
// wait for 1s maximum...
setTimeout(() => {
process.stdin.removeListener('data', dataListener);
resolve(false);
}, durationinMs);
// ...but finish early if we detect data
process.stdin.once('data', dataListener);
});
}
export function getStdinFilePath(): string {
return paths.join(os.tmpdir(), `code-stdin-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 3)}.txt`);
}
export async function readFromStdin(targetPath: string, verbose: boolean): Promise<void> {
// open tmp file for writing
const stdinFileStream = fs.createWriteStream(targetPath);
let encoding = await resolveTerminalEncoding(verbose);
const iconv = await import('iconv-lite-umd');
if (!iconv.encodingExists(encoding)) {
console.log(`Unsupported terminal encoding: ${encoding}, falling back to UTF-8.`);
encoding = 'utf8';
}
// Pipe into tmp file using terminals encoding
const decoder = iconv.getDecoder(encoding);
process.stdin.on('data', chunk => stdinFileStream.write(decoder.write(chunk)));
process.stdin.on('end', () => {
const end = decoder.end();
if (typeof end === 'string') {
stdinFileStream.write(end);
}
stdinFileStream.end();
});
process.stdin.on('error', error => stdinFileStream.destroy(error));
process.stdin.on('close', () => stdinFileStream.close());
}

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small.
*/
import * as path from 'vs/base/common/path';
import * as os from 'os';
import * as fs from 'fs';
export function createWaitMarkerFile(verbose?: boolean): string | undefined {
const randomWaitMarkerPath = path.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10));
try {
fs.writeFileSync(randomWaitMarkerPath, ''); // use built-in fs to avoid dragging in more dependencies
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;
}
}

View File

@@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as path from 'vs/base/common/path';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { parseExtensionHostPort, parseUserDataDir } from 'vs/platform/environment/node/environmentService';
suite('EnvironmentService', () => {
test('parseExtensionHostPort when built', () => {
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a, OPTIONS), true);
assert.deepEqual(parse([]), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost']), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost=1234']), { port: 1234, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost']), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost=5678']), { port: 5678, break: true, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost=1234', '--debugBrkPluginHost=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' });
assert.deepEqual(parse(['--inspect-extensions']), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--inspect-extensions=1234']), { port: 1234, break: false, debugId: undefined });
assert.deepEqual(parse(['--inspect-brk-extensions']), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--inspect-brk-extensions=5678']), { port: 5678, break: true, debugId: undefined });
assert.deepEqual(parse(['--inspect-extensions=1234', '--inspect-brk-extensions=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' });
});
test('parseExtensionHostPort when unbuilt', () => {
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a, OPTIONS), false);
assert.deepEqual(parse([]), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost']), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost=1234']), { port: 1234, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost']), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost=5678']), { port: 5678, break: true, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost=1234', '--debugBrkPluginHost=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' });
assert.deepEqual(parse(['--inspect-extensions']), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--inspect-extensions=1234']), { port: 1234, break: false, debugId: undefined });
assert.deepEqual(parse(['--inspect-brk-extensions']), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--inspect-brk-extensions=5678']), { port: 5678, break: true, debugId: undefined });
assert.deepEqual(parse(['--inspect-extensions=1234', '--inspect-brk-extensions=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' });
});
test('userDataPath', () => {
const parse = (a: string[], b: { cwd: () => string, env: { [key: string]: string } }) => parseUserDataDir(parseArgs(a, OPTIONS), <any>b);
assert.equal(parse(['--user-data-dir', './dir'], { cwd: () => '/foo', env: {} }), path.resolve('/foo/dir'),
'should use cwd when --user-data-dir is specified');
assert.equal(parse(['--user-data-dir', './dir'], { cwd: () => '/foo', env: { 'VSCODE_CWD': '/bar' } }), path.resolve('/bar/dir'),
'should use VSCODE_CWD as the cwd when --user-data-dir is specified');
});
// https://github.com/microsoft/vscode/issues/78440
test('careful with boolean file names', function () {
let actual = parseArgs(['-r', 'arg.txt'], OPTIONS);
assert(actual['reuse-window']);
assert.deepEqual(actual._, ['arg.txt']);
actual = parseArgs(['-r', 'true.txt'], OPTIONS);
assert(actual['reuse-window']);
assert.deepEqual(actual._, ['true.txt']);
});
});