Merge VS Code 1.31.1 (#4283)

This commit is contained in:
Matt Irvine
2019-03-15 13:09:45 -07:00
committed by GitHub
parent 7d31575149
commit 86bac90001
1716 changed files with 53308 additions and 48375 deletions

View File

@@ -0,0 +1,303 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { Registry } from 'vs/platform/registry/common/platform';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { workspaceSettingsSchemaId, launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { isObject } from 'vs/base/common/types';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
const configurationEntrySchema: IJSONSchema = {
type: 'object',
defaultSnippets: [{ body: { title: '', properties: {} } }],
properties: {
title: {
description: nls.localize('vscode.extension.contributes.configuration.title', 'A summary of the settings. This label will be used in the settings file as separating comment.'),
type: 'string'
},
properties: {
description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'),
type: 'object',
additionalProperties: {
anyOf: [
{ $ref: 'http://json-schema.org/draft-07/schema#' },
{
type: 'object',
properties: {
isExecutable: {
type: 'boolean',
deprecationMessage: 'This property is deprecated. Instead use `scope` property and set it to `application` value.'
},
scope: {
type: 'string',
enum: ['application', 'window', 'resource'],
default: 'window',
enumDescriptions: [
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in User settings."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the User or Workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.")
],
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.")
},
enumDescriptions: {
type: 'array',
items: {
type: 'string',
},
description: nls.localize('scope.enumDescriptions', 'Descriptions for enum values')
},
markdownEnumDescription: {
type: 'array',
items: {
type: 'string',
},
description: nls.localize('scope.markdownEnumDescription', 'Descriptions for enum values in the markdown format.')
},
markdownDescription: {
type: 'string',
description: nls.localize('scope.markdownDescription', 'The description in the markdown format.')
},
deprecationMessage: {
type: 'string',
description: nls.localize('scope.deprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation.')
}
}
}
]
}
}
}
};
// BEGIN VSCode extension point `configurationDefaults`
const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>({
extensionPoint: 'configurationDefaults',
jsonSchema: {
description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'),
type: 'object',
patternProperties: {
'\\[.*\\]$': {
type: 'object',
default: {},
$ref: editorConfigurationSchemaId,
}
}
},
isDynamic: true
});
defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {
if (removed.length) {
const removedDefaultConfigurations: IDefaultConfigurationExtension[] = removed.map(extension => {
const id = extension.description.identifier;
const name = extension.description.name;
const defaults = objects.deepClone(extension.value);
return <IDefaultConfigurationExtension>{
id, name, defaults
};
});
configurationRegistry.deregisterDefaultConfigurations(removedDefaultConfigurations);
}
if (added.length) {
const addedDefaultConfigurations = added.map(extension => {
const id = extension.description.identifier;
const name = extension.description.name;
const defaults = objects.deepClone(extension.value);
return <IDefaultConfigurationExtension>{
id, name, defaults
};
});
configurationRegistry.registerDefaultConfigurations(addedDefaultConfigurations);
}
});
// END VSCode extension point `configurationDefaults`
// BEGIN VSCode extension point `configuration`
const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>({
extensionPoint: 'configuration',
deps: [defaultConfigurationExtPoint],
jsonSchema: {
description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'),
oneOf: [
configurationEntrySchema,
{
type: 'array',
items: configurationEntrySchema
}
]
},
isDynamic: true
});
const extensionConfigurations: Map<string, IConfigurationNode[]> = new Map<string, IConfigurationNode[]>();
configurationExtPoint.setHandler((extensions, { added, removed }) => {
if (removed.length) {
const removedConfigurations: IConfigurationNode[] = [];
for (const extension of removed) {
const key = ExtensionIdentifier.toKey(extension.description.identifier);
removedConfigurations.push(...(extensionConfigurations.get(key) || []));
extensionConfigurations.delete(key);
}
configurationRegistry.deregisterConfigurations(removedConfigurations);
}
function handleConfiguration(node: IConfigurationNode, extension: IExtensionPointUser<any>): IConfigurationNode[] {
const configurations: IConfigurationNode[] = [];
let configuration = objects.deepClone(node);
if (configuration.title && (typeof configuration.title !== 'string')) {
extension.collector.error(nls.localize('invalid.title', "'configuration.title' must be a string"));
}
validateProperties(configuration, extension);
configuration.id = node.id || extension.description.identifier.value;
configuration.contributedByExtension = true;
configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value;
configurations.push(configuration);
return configurations;
}
if (added.length) {
const addedConfigurations: IConfigurationNode[] = [];
for (let extension of added) {
const configurations: IConfigurationNode[] = [];
const value = <IConfigurationNode | IConfigurationNode[]>extension.value;
if (!Array.isArray(value)) {
configurations.push(...handleConfiguration(value, extension));
} else {
value.forEach(v => configurations.push(...handleConfiguration(v, extension)));
}
extensionConfigurations.set(ExtensionIdentifier.toKey(extension.description.identifier), configurations);
addedConfigurations.push(...configurations);
}
configurationRegistry.registerConfigurations(addedConfigurations, false);
}
});
// END VSCode extension point `configuration`
function validateProperties(configuration: IConfigurationNode, extension: IExtensionPointUser<any>): void {
let properties = configuration.properties;
if (properties) {
if (typeof properties !== 'object') {
extension.collector.error(nls.localize('invalid.properties', "'configuration.properties' must be an object"));
configuration.properties = {};
}
for (let key in properties) {
const message = validateProperty(key);
if (message) {
delete properties[key];
extension.collector.warn(message);
continue;
}
const propertyConfiguration = properties[key];
if (!isObject(propertyConfiguration)) {
delete properties[key];
extension.collector.error(nls.localize('invalid.property', "'configuration.property' must be an object"));
continue;
}
if (propertyConfiguration.scope) {
if (propertyConfiguration.scope.toString() === 'application') {
propertyConfiguration.scope = ConfigurationScope.APPLICATION;
} else if (propertyConfiguration.scope.toString() === 'resource') {
propertyConfiguration.scope = ConfigurationScope.RESOURCE;
} else {
propertyConfiguration.scope = ConfigurationScope.WINDOW;
}
} else {
propertyConfiguration.scope = ConfigurationScope.WINDOW;
}
}
}
let subNodes = configuration.allOf;
if (subNodes) {
extension.collector.error(nls.localize('invalid.allOf', "'configuration.allOf' is deprecated and should no longer be used. Instead, pass multiple configuration sections as an array to the 'configuration' contribution point."));
for (let node of subNodes) {
validateProperties(node, extension);
}
}
}
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
allowComments: true,
default: {
folders: [
{
path: ''
}
],
settings: {
}
},
required: ['folders'],
properties: {
'folders': {
minItems: 0,
uniqueItems: true,
description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace."),
items: {
type: 'object',
default: { path: '' },
oneOf: [{
properties: {
path: {
type: 'string',
description: nls.localize('workspaceConfig.path.description', "A file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file.")
},
name: {
type: 'string',
description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ")
}
},
required: ['path']
}, {
properties: {
uri: {
type: 'string',
description: nls.localize('workspaceConfig.uri.description', "URI of the folder")
},
name: {
type: 'string',
description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ")
}
},
required: ['uri']
}]
}
},
'settings': {
type: 'object',
default: {},
description: nls.localize('workspaceConfig.settings.description', "Workspace settings"),
$ref: workspaceSettingsSchemaId
},
'launch': {
type: 'object',
default: { configurations: [], compounds: [] },
description: nls.localize('workspaceConfig.launch.description', "Workspace launch configurations"),
$ref: launchSchemaId
},
'extensions': {
type: 'object',
default: {},
description: nls.localize('workspaceConfig.extensions.description', "Workspace extensions"),
$ref: 'vscode://schemas/extensions'
}
},
additionalProperties: false,
errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property")
});

View File

@@ -0,0 +1,398 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import * as resources from 'vs/base/common/resources';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { forEach } from 'vs/base/common/collections';
import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { MenuId, MenuRegistry, ILocalizedString, IMenuItem } from 'vs/platform/actions/common/actions';
import { URI } from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
namespace schema {
// --- menus contribution point
export interface IUserFriendlyMenuItem {
command: string;
alt?: string;
when?: string;
group?: string;
}
export function parseMenuId(value: string): MenuId | undefined {
switch (value) {
case 'commandPalette': return MenuId.CommandPalette;
case 'touchBar': return MenuId.TouchBarContext;
case 'editor/title': return MenuId.EditorTitle;
case 'editor/context': return MenuId.EditorContext;
case 'explorer/context': return MenuId.ExplorerContext;
case 'editor/title/context': return MenuId.EditorTitleContext;
case 'debug/callstack/context': return MenuId.DebugCallStackContext;
case 'scm/title': return MenuId.SCMTitle;
case 'scm/sourceControl': return MenuId.SCMSourceControl;
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
case 'scm/resourceState/context': return MenuId.SCMResourceContext;
case 'scm/change/title': return MenuId.SCMChangeContext;
case 'view/title': return MenuId.ViewTitle;
case 'view/item/context': return MenuId.ViewItemContext;
// {{SQL CARBON EDIT}}
case 'objectExplorer/item/context': return MenuId.ObjectExplorerItemContext;
case 'notebook/toolbar': return MenuId.NotebookToolbar;
case 'dataExplorer/context': return MenuId.DataExplorerContext;
}
return undefined;
}
export function isValidMenuItems(menu: IUserFriendlyMenuItem[], collector: ExtensionMessageCollector): boolean {
if (!Array.isArray(menu)) {
collector.error(localize('requirearray', "menu items must be an array"));
return false;
}
for (let item of menu) {
if (typeof item.command !== 'string') {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command'));
return false;
}
if (item.alt && typeof item.alt !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt'));
return false;
}
if (item.when && typeof item.when !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
return false;
}
if (item.group && typeof item.group !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group'));
return false;
}
}
return true;
}
const menuItem: IJSONSchema = {
type: 'object',
properties: {
command: {
description: localize('vscode.extension.contributes.menuItem.command', 'Identifier of the command to execute. The command must be declared in the \'commands\'-section'),
type: 'string'
},
alt: {
description: localize('vscode.extension.contributes.menuItem.alt', 'Identifier of an alternative command to execute. The command must be declared in the \'commands\'-section'),
type: 'string'
},
when: {
description: localize('vscode.extension.contributes.menuItem.when', 'Condition which must be true to show this item'),
type: 'string'
},
group: {
description: localize('vscode.extension.contributes.menuItem.group', 'Group into which this command belongs'),
type: 'string'
}
}
};
export const menusContribtion: IJSONSchema = {
description: localize('vscode.extension.contributes.menus', "Contributes menu items to the editor"),
type: 'object',
properties: {
'commandPalette': {
description: localize('menus.commandPalette', "The Command Palette"),
type: 'array',
items: menuItem
},
'touchBar': {
description: localize('menus.touchBar', "The touch bar (macOS only)"),
type: 'array',
items: menuItem
},
'editor/title': {
description: localize('menus.editorTitle', "The editor title menu"),
type: 'array',
items: menuItem
},
'editor/context': {
description: localize('menus.editorContext', "The editor context menu"),
type: 'array',
items: menuItem
},
'explorer/context': {
description: localize('menus.explorerContext', "The file explorer context menu"),
type: 'array',
items: menuItem
},
'editor/title/context': {
description: localize('menus.editorTabContext', "The editor tabs context menu"),
type: 'array',
items: menuItem
},
'debug/callstack/context': {
description: localize('menus.debugCallstackContext', "The debug callstack context menu"),
type: 'array',
items: menuItem
},
'scm/title': {
description: localize('menus.scmTitle', "The Source Control title menu"),
type: 'array',
items: menuItem
},
'scm/sourceControl': {
description: localize('menus.scmSourceControl', "The Source Control menu"),
type: 'array',
items: menuItem
},
'scm/resourceGroup/context': {
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu"),
type: 'array',
items: menuItem
},
'scm/resourceState/context': {
description: localize('menus.resourceStateContext', "The Source Control resource state context menu"),
type: 'array',
items: menuItem
},
'view/title': {
description: localize('view.viewTitle', "The contributed view title menu"),
type: 'array',
items: menuItem
},
'view/item/context': {
description: localize('view.itemContext', "The contributed view item context menu"),
type: 'array',
items: menuItem
}
}
};
// --- commands contribution point
export interface IUserFriendlyCommand {
command: string;
title: string | ILocalizedString;
category?: string | ILocalizedString;
icon?: IUserFriendlyIcon;
}
export type IUserFriendlyIcon = string | { light: string; dark: string; };
export function isValidCommand(command: IUserFriendlyCommand, collector: ExtensionMessageCollector): boolean {
if (!command) {
collector.error(localize('nonempty', "expected non-empty value."));
return false;
}
if (isFalsyOrWhitespace(command.command)) {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command'));
return false;
}
if (!isValidLocalizedString(command.title, collector, 'title')) {
return false;
}
if (command.category && !isValidLocalizedString(command.category, collector, 'category')) {
return false;
}
if (!isValidIcon(command.icon, collector)) {
return false;
}
return true;
}
function isValidIcon(icon: IUserFriendlyIcon | undefined, collector: ExtensionMessageCollector): boolean {
if (typeof icon === 'undefined') {
return true;
}
if (typeof icon === 'string') {
return true;
} else if (typeof icon.dark === 'string' && typeof icon.light === 'string') {
return true;
}
collector.error(localize('opticon', "property `icon` can be omitted or must be either a string or a literal like `{dark, light}`"));
return false;
}
function isValidLocalizedString(localized: string | ILocalizedString, collector: ExtensionMessageCollector, propertyName: string): boolean {
if (typeof localized === 'undefined') {
collector.error(localize('requireStringOrObject', "property `{0}` is mandatory and must be of type `string` or `object`", propertyName));
return false;
} else if (typeof localized === 'string' && isFalsyOrWhitespace(localized)) {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", propertyName));
return false;
} else if (typeof localized !== 'string' && (isFalsyOrWhitespace(localized.original) || isFalsyOrWhitespace(localized.value))) {
collector.error(localize('requirestrings', "properties `{0}` and `{1}` are mandatory and must be of type `string`", `${propertyName}.value`, `${propertyName}.original`));
return false;
}
return true;
}
const commandType: IJSONSchema = {
type: 'object',
required: ['command', 'title'],
properties: {
command: {
description: localize('vscode.extension.contributes.commandType.command', 'Identifier of the command to execute'),
type: 'string'
},
title: {
description: localize('vscode.extension.contributes.commandType.title', 'Title by which the command is represented in the UI'),
type: 'string'
},
category: {
description: localize('vscode.extension.contributes.commandType.category', '(Optional) Category string by the command is grouped in the UI'),
type: 'string'
},
icon: {
description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path or a themable configuration'),
anyOf: [{
type: 'string'
},
{
type: 'object',
properties: {
light: {
description: localize('vscode.extension.contributes.commandType.icon.light', 'Icon path when a light theme is used'),
type: 'string'
},
dark: {
description: localize('vscode.extension.contributes.commandType.icon.dark', 'Icon path when a dark theme is used'),
type: 'string'
}
}
}]
}
}
};
export const commandsContribution: IJSONSchema = {
description: localize('vscode.extension.contributes.commands', "Contributes commands to the command palette."),
oneOf: [
commandType,
{
type: 'array',
items: commandType
}
]
};
}
let _commandRegistrations: IDisposable[] = [];
ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>({
extensionPoint: 'commands',
jsonSchema: schema.commandsContribution,
isDynamic: true
}).setHandler(extensions => {
function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser<any>, disposables: IDisposable[]) {
if (!schema.isValidCommand(userFriendlyCommand, extension.collector)) {
return;
}
const { icon, category, title, command } = userFriendlyCommand;
let absoluteIcon: { dark: URI; light?: URI; } | undefined;
if (icon) {
if (typeof icon === 'string') {
absoluteIcon = { dark: resources.joinPath(extension.description.extensionLocation, icon) };
} else {
absoluteIcon = {
dark: resources.joinPath(extension.description.extensionLocation, icon.dark),
light: resources.joinPath(extension.description.extensionLocation, icon.light)
};
}
}
if (MenuRegistry.getCommand(command)) {
extension.collector.info(localize('dup', "Command `{0}` appears multiple times in the `commands` section.", userFriendlyCommand.command));
}
const registration = MenuRegistry.addCommand({ id: command, title, category, iconLocation: absoluteIcon });
disposables.push(registration);
}
// remove all previous command registrations
_commandRegistrations = dispose(_commandRegistrations);
for (let extension of extensions) {
const { value } = extension;
if (Array.isArray<schema.IUserFriendlyCommand>(value)) {
for (let command of value) {
handleCommand(command, extension, _commandRegistrations);
}
} else {
handleCommand(value, extension, _commandRegistrations);
}
}
});
let _menuRegistrations: IDisposable[] = [];
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>({
extensionPoint: 'menus',
jsonSchema: schema.menusContribtion,
isDynamic: true
}).setHandler(extensions => {
// remove all previous menu registrations
_menuRegistrations = dispose(_menuRegistrations);
for (let extension of extensions) {
const { value, collector } = extension;
forEach(value, entry => {
if (!schema.isValidMenuItems(entry.value, collector)) {
return;
}
const menu = schema.parseMenuId(entry.key);
if (typeof menu !== 'number') {
collector.warn(localize('menuId.invalid', "`{0}` is not a valid menu identifier", entry.key));
return;
}
for (let item of entry.value) {
let command = MenuRegistry.getCommand(item.command);
let alt = item.alt && MenuRegistry.getCommand(item.alt);
if (!command) {
collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command));
continue;
}
if (item.alt && !alt) {
collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", item.alt));
}
if (item.command === item.alt) {
collector.info(localize('dupe.command', "Menu item references the same command as default and alt-command"));
}
let group: string | undefined;
let order: number | undefined;
if (item.group) {
const idx = item.group.lastIndexOf('@');
if (idx > 0) {
group = item.group.substr(0, idx);
order = Number(item.group.substr(idx + 1)) || undefined;
} else {
group = item.group;
}
}
const registration = MenuRegistry.appendMenuItem(menu, {
command,
alt,
group,
order,
when: ContextKeyExpr.deserialize(item.when)
} as IMenuItem);
_menuRegistrations.push(registration);
}
});
}
});