Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -3,13 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { addClasses, createCSSRule, removeClasses } from 'vs/base/browser/dom';
import { addClasses, createCSSRule, removeClasses, asDomUri } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAction } from 'vs/base/common/actions';
import { Emitter } from 'vs/base/common/event';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable, MutableDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
@@ -20,7 +20,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
// The alternative key on all platforms is alt. On windows we also support shift as an alternative key #44136
class AlternativeKeyEmitter extends Emitter<boolean> {
private _subscriptions: IDisposable[] = [];
private readonly _subscriptions = new DisposableStore();
private _isPressed: boolean;
private static instance: AlternativeKeyEmitter;
private _suppressAltKeyUp: boolean = false;
@@ -28,10 +28,10 @@ class AlternativeKeyEmitter extends Emitter<boolean> {
private constructor(contextMenuService: IContextMenuService) {
super();
this._subscriptions.push(domEvent(document.body, 'keydown')(e => {
this._subscriptions.add(domEvent(document.body, 'keydown')(e => {
this.isPressed = e.altKey || ((isWindows || isLinux) && e.shiftKey);
}));
this._subscriptions.push(domEvent(document.body, 'keyup')(e => {
this._subscriptions.add(domEvent(document.body, 'keyup')(e => {
if (this.isPressed) {
if (this._suppressAltKeyUp) {
e.preventDefault();
@@ -41,10 +41,10 @@ class AlternativeKeyEmitter extends Emitter<boolean> {
this._suppressAltKeyUp = false;
this.isPressed = false;
}));
this._subscriptions.push(domEvent(document.body, 'mouseleave')(e => this.isPressed = false));
this._subscriptions.push(domEvent(document.body, 'blur')(e => this.isPressed = false));
this._subscriptions.add(domEvent(document.body, 'mouseleave')(e => this.isPressed = false));
this._subscriptions.add(domEvent(document.body, 'blur')(e => this.isPressed = false));
// Workaround since we do not get any events while a context menu is shown
this._subscriptions.push(contextMenuService.onDidContextMenu(() => this.isPressed = false));
this._subscriptions.add(contextMenuService.onDidContextMenu(() => this.isPressed = false));
}
get isPressed(): boolean {
@@ -72,25 +72,36 @@ class AlternativeKeyEmitter extends Emitter<boolean> {
dispose() {
super.dispose();
this._subscriptions = dispose(this._subscriptions);
this._subscriptions.dispose();
}
}
export function fillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, contextMenuService: IContextMenuService, isPrimaryGroup?: (group: string) => boolean): void {
export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, contextMenuService: IContextMenuService, isPrimaryGroup?: (group: string) => boolean): IDisposable {
const groups = menu.getActions(options);
const getAlternativeActions = AlternativeKeyEmitter.getInstance(contextMenuService).isPressed;
fillInActions(groups, target, getAlternativeActions, isPrimaryGroup);
const useAlternativeActions = AlternativeKeyEmitter.getInstance(contextMenuService).isPressed;
fillInActions(groups, target, useAlternativeActions, isPrimaryGroup);
return asDisposable(groups);
}
export function fillInActionBarActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): void {
export function createAndFillInActionBarActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable {
const groups = menu.getActions(options);
// Action bars handle alternative actions on their own so the alternative actions should be ignored
fillInActions(groups, target, false, isPrimaryGroup);
return asDisposable(groups);
}
function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray<MenuItemAction | SubmenuItemAction>]>): IDisposable {
const disposables = new DisposableStore();
for (const [, actions] of groups) {
for (const action of actions) {
disposables.add(action);
}
}
return disposables;
}
// {{SQL CARBON EDIT}} add export modifier
export function fillInActions(groups: [string, Array<MenuItemAction | SubmenuItemAction>][], target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
export function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray<MenuItemAction | SubmenuItemAction>]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
for (let tuple of groups) {
let [group, actions] = tuple;
if (useAlternativeActions) {
@@ -128,7 +139,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
static readonly ICON_PATH_TO_CSS_RULES: Map<string /* path*/, string /* CSS rule */> = new Map<string, string>();
private _wantsAltCommand: boolean;
private _itemClassDispose?: IDisposable;
private readonly _itemClassDispose = this._register(new MutableDisposable());
private readonly _altKey: AlternativeKeyEmitter;
constructor(
@@ -223,8 +234,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
}
_updateItemClass(item: ICommandAction): void {
dispose(this._itemClassDispose);
this._itemClassDispose = undefined;
this._itemClassDispose.value = undefined;
if (item.iconLocation) {
let iconClass: string;
@@ -235,24 +245,15 @@ export class MenuEntryActionViewItem extends ActionViewItem {
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
} else {
iconClass = ids.nextId();
createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${item.iconLocation.dark.toString()}")`);
createCSSRule(`.icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.dark).toString()}")`);
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
}
addClasses(this.label, 'icon', iconClass);
this._itemClassDispose = toDisposable(() => removeClasses(this.label, 'icon', iconClass));
this._itemClassDispose.value = toDisposable(() => removeClasses(this.label, 'icon', iconClass));
}
}
dispose(): void {
if (this._itemClassDispose) {
dispose(this._itemClassDispose);
this._itemClassDispose = undefined;
}
super.dispose();
}
}
// Need to subclass MenuEntryActionViewItem in order to respect

View File

@@ -103,7 +103,8 @@ export const enum MenuId {
CommentThreadTitle,
CommentThreadActions,
CommentTitle,
CommentActions
CommentActions,
GlobalActivity
}
export interface IMenuActionOptions {
@@ -125,64 +126,61 @@ export interface IMenuService {
createMenu(id: MenuId, scopedKeybindingService: IContextKeyService): IMenu;
}
export type ICommandsMap = Map<string, ICommandAction>;
export interface IMenuRegistry {
addCommand(userCommand: ICommandAction): IDisposable;
getCommand(id: string): ICommandAction;
getCommand(id: string): ICommandAction | undefined;
getCommands(): ICommandsMap;
appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;
getMenuItems(loc: MenuId): Array<IMenuItem | ISubmenuItem>;
readonly onDidChangeMenu: Event<MenuId>;
}
export interface ICommandsMap {
[id: string]: ICommandAction;
}
export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
private readonly _commands: { [id: string]: ICommandAction } = Object.create(null);
private readonly _menuItems: { [loc: number]: Array<IMenuItem | ISubmenuItem> } = Object.create(null);
private readonly _commands = new Map<string, ICommandAction>();
private readonly _menuItems = new Map<number, Array<IMenuItem | ISubmenuItem>>();
private readonly _onDidChangeMenu = new Emitter<MenuId>();
readonly onDidChangeMenu: Event<MenuId> = this._onDidChangeMenu.event;
addCommand(command: ICommandAction): IDisposable {
this._commands[command.id] = command;
this._commands.set(command.id, command);
this._onDidChangeMenu.fire(MenuId.CommandPalette);
return {
dispose: () => {
if (delete this._commands[command.id]) {
if (this._commands.delete(command.id)) {
this._onDidChangeMenu.fire(MenuId.CommandPalette);
}
}
};
}
getCommand(id: string): ICommandAction {
return this._commands[id];
getCommand(id: string): ICommandAction | undefined {
return this._commands.get(id);
}
getCommands(): ICommandsMap {
const result: ICommandsMap = Object.create(null);
for (const key in this._commands) {
result[key] = this.getCommand(key);
}
return result;
const map = new Map<string, ICommandAction>();
this._commands.forEach((value, key) => map.set(key, value));
return map;
}
appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable {
let array = this._menuItems[id];
let array = this._menuItems.get(id);
if (!array) {
this._menuItems[id] = array = [item];
array = [item];
this._menuItems.set(id, array);
} else {
array.push(item);
}
this._onDidChangeMenu.fire(id);
return {
dispose: () => {
const idx = array.indexOf(item);
const idx = array!.indexOf(item);
if (idx >= 0) {
array.splice(idx, 1);
array!.splice(idx, 1);
this._onDidChangeMenu.fire(id);
}
}
@@ -190,7 +188,7 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
}
getMenuItems(id: MenuId): Array<IMenuItem | ISubmenuItem> {
const result = (this._menuItems[id] || []).slice(0);
const result = (this._menuItems.get(id) || []).slice(0);
if (id === MenuId.CommandPalette) {
// CommandPalette is special because it shows
@@ -211,11 +209,11 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
set.add(alt.id);
}
}
for (let id in this._commands) {
this._commands.forEach((command, id) => {
if (!set.has(id)) {
result.push({ command: this._commands[id] });
result.push({ command });
}
}
});
}
};
@@ -268,6 +266,13 @@ export class MenuItemAction extends ExecuteCommandAction {
this.alt = alt ? new MenuItemAction(alt, undefined, this._options, contextKeyService, commandService) : undefined;
}
dispose(): void {
if (this.alt) {
this.alt.dispose();
}
super.dispose();
}
run(...args: any[]): Promise<any> {
let runArgs: any[] = [];

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey';
@@ -27,10 +27,9 @@ export class MenuService implements IMenuService {
type MenuItemGroup = [string, Array<IMenuItem | ISubmenuItem>];
class Menu implements IMenu {
class Menu extends Disposable implements IMenu {
private readonly _onDidChange = new Emitter<IMenu | undefined>();
private readonly _disposables: IDisposable[] = [];
private readonly _onDidChange = this._register(new Emitter<IMenu | undefined>());
private _menuGroups: MenuItemGroup[];
private _contextKeys: Set<string>;
@@ -40,23 +39,24 @@ class Menu implements IMenu {
@ICommandService private readonly _commandService: ICommandService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService
) {
super();
this._build();
// rebuild this menu whenever the menu registry reports an
// event for this MenuId
Event.debounce(
this._register(Event.debounce(
Event.filter(MenuRegistry.onDidChangeMenu, menuId => menuId === this._id),
() => { },
50
)(this._build, this, this._disposables);
)(this._build, this));
// when context keys change we need to check if the menu also
// has changed
Event.debounce<IContextKeyChangeEvent, boolean>(
this._register(Event.debounce<IContextKeyChangeEvent, boolean>(
this._contextKeyService.onDidChangeContext,
(last, event) => last || event.affectsSome(this._contextKeys),
50
)(e => e && this._onDidChange.fire(undefined), this, this._disposables);
)(e => e && this._onDidChange.fire(undefined), this));
}
private _build(): void {
@@ -95,11 +95,6 @@ class Menu implements IMenu {
this._onDidChange.fire(this);
}
dispose() {
dispose(this._disposables);
this._onDidChange.dispose();
}
get onDidChange(): Event<IMenu | undefined> {
return this._onDidChange.event;
}

View File

@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions';
import { MenuService } from 'vs/platform/actions/common/menuService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { NullCommandService } from 'vs/platform/commands/common/commands';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
@@ -23,42 +23,42 @@ const contextKeyService = new class extends MockContextKeyService {
suite('MenuService', function () {
let menuService: MenuService;
let disposables: IDisposable[];
const disposables = new DisposableStore();
let testMenuId: MenuId;
setup(function () {
menuService = new MenuService(NullCommandService);
testMenuId = Math.PI;
disposables = [];
disposables.clear();
});
teardown(function () {
dispose(disposables);
disposables.clear();
});
test('group sorting', function () {
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'one', title: 'FOO' },
group: '0_hello'
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'two', title: 'FOO' },
group: 'hello'
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'three', title: 'FOO' },
group: 'Hello'
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'four', title: 'FOO' },
group: ''
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'five', title: 'FOO' },
group: 'navigation'
}));
@@ -77,17 +77,17 @@ suite('MenuService', function () {
test('in group sorting, by title', function () {
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'a', title: 'aaa' },
group: 'Hello'
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'b', title: 'fff' },
group: 'Hello'
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'c', title: 'zzz' },
group: 'Hello'
}));
@@ -106,24 +106,24 @@ suite('MenuService', function () {
test('in group sorting, by title and order', function () {
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'a', title: 'aaa' },
group: 'Hello',
order: 10
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'b', title: 'fff' },
group: 'Hello'
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'c', title: 'zzz' },
group: 'Hello',
order: -1
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'd', title: 'yyy' },
group: 'Hello',
order: -1
@@ -145,19 +145,19 @@ suite('MenuService', function () {
test('in group sorting, special: navigation', function () {
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'a', title: 'aaa' },
group: 'navigation',
order: 1.3
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'b', title: 'fff' },
group: 'navigation',
order: 1.2
}));
disposables.push(MenuRegistry.appendMenuItem(testMenuId, {
disposables.add(MenuRegistry.appendMenuItem(testMenuId, {
command: { id: 'c', title: 'zzz' },
group: 'navigation',
order: 1.1
@@ -177,7 +177,7 @@ suite('MenuService', function () {
test('special MenuId palette', function () {
disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: { id: 'a', title: 'Explicit' }
}));