mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 17:23:40 -05:00
Merge VS Code 1.31.1 (#4283)
This commit is contained in:
303
src/vs/workbench/api/common/configurationExtensionPoint.ts
Normal file
303
src/vs/workbench/api/common/configurationExtensionPoint.ts
Normal 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")
|
||||
});
|
||||
398
src/vs/workbench/api/common/menusExtensionPoint.ts
Normal file
398
src/vs/workbench/api/common/menusExtensionPoint.ts
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user