chore(vscode): update to 1.55.2

This commit is contained in:
Akash Satheesan
2021-04-09 11:32:27 +05:30
1102 changed files with 39988 additions and 23544 deletions

View File

@@ -7,9 +7,9 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, getSelectionKeyboardEvent, WorkbenchListWidget } from 'vs/platform/list/browser/listService';
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, getSelectionKeyboardEvent, WorkbenchListWidget, WorkbenchListSelectionNavigation } from 'vs/platform/list/browser/listService';
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
import { range } from 'vs/base/common/arrays';
import { equals, range } from 'vs/base/common/arrays';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
@@ -28,36 +28,40 @@ function ensureDOMFocus(widget: ListWidget | undefined): void {
}
}
function focusDown(accessor: ServicesAccessor, arg2?: number, loop: boolean = false): void {
const focused = accessor.get(IListService).lastFocusedList;
const count = typeof arg2 === 'number' ? arg2 : 1;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
const list = focused;
list.focusNext(count);
const listFocus = list.getFocus();
if (listFocus.length) {
list.reveal(listFocus[0]);
}
async function updateFocus(widget: WorkbenchListWidget, updateFocusFn: (widget: WorkbenchListWidget) => void | Promise<void>): Promise<void> {
if (!WorkbenchListSelectionNavigation.getValue(widget.contextKeyService)) {
return updateFocusFn(widget);
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
const focus = widget.getFocus();
const selection = widget.getSelection();
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.focusNext(count, loop, fakeKeyboardEvent);
await updateFocusFn(widget);
const listFocus = tree.getFocus();
if (listFocus.length) {
tree.reveal(listFocus[0]);
}
const newFocus = widget.getFocus();
if (selection.length > 1 || !equals(focus, selection) || equals(focus, newFocus)) {
return;
}
// Ensure DOM Focus
ensureDOMFocus(focused);
const fakeKeyboardEvent = new KeyboardEvent('keydown');
widget.setSelection(newFocus, fakeKeyboardEvent);
}
async function navigate(widget: WorkbenchListWidget | undefined, updateFocusFn: (widget: WorkbenchListWidget) => void | Promise<void>): Promise<void> {
if (!widget) {
return;
}
await updateFocus(widget, updateFocusFn);
const listFocus = widget.getFocus();
if (listFocus.length) {
widget.reveal(listFocus[0]);
}
ensureDOMFocus(widget);
}
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -69,7 +73,81 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
primary: KeyCode.DownArrow,
secondary: [KeyMod.WinCtrl | KeyCode.KEY_N]
},
handler: (accessor, arg2) => focusDown(accessor, arg2)
handler: (accessor, arg2) => {
navigate(accessor.get(IListService).lastFocusedList, async widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
await widget.focusNext(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusUp',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.UpArrow,
mac: {
primary: KeyCode.UpArrow,
secondary: [KeyMod.WinCtrl | KeyCode.KEY_P]
},
handler: (accessor, arg2) => {
navigate(accessor.get(IListService).lastFocusedList, async widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
await widget.focusPrevious(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusPageDown',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.PageDown,
handler: (accessor) => {
navigate(accessor.get(IListService).lastFocusedList, async widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
await widget.focusNextPage(fakeKeyboardEvent);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusPageUp',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.PageUp,
handler: (accessor) => {
navigate(accessor.get(IListService).lastFocusedList, async widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
await widget.focusPreviousPage(fakeKeyboardEvent);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusFirst',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.Home,
handler: (accessor) => {
navigate(accessor.get(IListService).lastFocusedList, async widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
await widget.focusFirst(fakeKeyboardEvent);
});
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusLast',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.End,
handler: (accessor) => {
navigate(accessor.get(IListService).lastFocusedList, async widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
await widget.focusLast(fakeKeyboardEvent);
});
}
});
function expandMultiSelection(focused: WorkbenchListWidget, previousFocus: unknown): void {
@@ -116,64 +194,28 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey),
primary: KeyMod.Shift | KeyCode.DownArrow,
handler: (accessor, arg2) => {
const focused = accessor.get(IListService).lastFocusedList;
const widget = accessor.get(IListService).lastFocusedList;
// List / Tree
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const list = focused;
// Focus down first
const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined;
focusDown(accessor, arg2, false);
// Then adjust selection
expandMultiSelection(focused, previousFocus);
if (!widget) {
return;
}
}
});
function focusUp(accessor: ServicesAccessor, arg2?: number, loop: boolean = false): void {
const focused = accessor.get(IListService).lastFocusedList;
const count = typeof arg2 === 'number' ? arg2 : 1;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
const list = focused;
list.focusPrevious(count);
const listFocus = list.getFocus();
if (listFocus.length) {
list.reveal(listFocus[0]);
}
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
// Focus down first
const previousFocus = widget.getFocus() ? widget.getFocus()[0] : undefined;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.focusPrevious(count, loop, fakeKeyboardEvent);
widget.focusNext(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
const listFocus = tree.getFocus();
if (listFocus.length) {
tree.reveal(listFocus[0]);
// Then adjust selection
expandMultiSelection(widget, previousFocus);
const focus = widget.getFocus();
if (focus.length) {
widget.reveal(focus[0]);
}
ensureDOMFocus(widget);
}
// Ensure DOM Focus
ensureDOMFocus(focused);
}
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusUp',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.UpArrow,
mac: {
primary: KeyCode.UpArrow,
secondary: [KeyMod.WinCtrl | KeyCode.KEY_P]
},
handler: (accessor, arg2) => focusUp(accessor, arg2)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -182,19 +224,27 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey),
primary: KeyMod.Shift | KeyCode.UpArrow,
handler: (accessor, arg2) => {
const focused = accessor.get(IListService).lastFocusedList;
const widget = accessor.get(IListService).lastFocusedList;
// List / Tree
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const list = focused;
// Focus up first
const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined;
focusUp(accessor, arg2, false);
// Then adjust selection
expandMultiSelection(focused, previousFocus);
if (!widget) {
return;
}
// Focus up first
const previousFocus = widget.getFocus() ? widget.getFocus()[0] : undefined;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
widget.focusPrevious(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
// Then adjust selection
expandMultiSelection(widget, previousFocus);
const focus = widget.getFocus();
if (focus.length) {
widget.reveal(focus[0]);
}
ensureDOMFocus(widget);
}
});
@@ -208,29 +258,29 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow]
},
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
const widget = accessor.get(IListService).lastFocusedList;
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList || focused instanceof Table)) {
if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
const focusedElements = tree.getFocus();
if (!widget || !(widget instanceof ObjectTree || widget instanceof DataTree || widget instanceof AsyncDataTree)) {
return;
}
if (focusedElements.length === 0) {
return;
}
const tree = widget;
const focusedElements = tree.getFocus();
const focus = focusedElements[0];
if (focusedElements.length === 0) {
return;
}
if (!tree.collapse(focus)) {
const parent = tree.getParentElement(focus);
const focus = focusedElements[0];
if (parent) {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.setFocus([parent], fakeKeyboardEvent);
tree.reveal(parent);
}
}
if (!tree.collapse(focus)) {
const parent = tree.getParentElement(focus);
if (parent) {
navigate(widget, widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
widget.setFocus([parent], fakeKeyboardEvent);
});
}
}
}
@@ -260,25 +310,24 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
const widget = accessor.get(IListService).lastFocusedList;
if (!focused || focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
if (!widget || !(widget instanceof ObjectTree || widget instanceof DataTree || widget instanceof AsyncDataTree)) {
return;
}
if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
const focusedElements = tree.getFocus();
if (focusedElements.length === 0) {
return;
}
const focus = focusedElements[0];
const parent = tree.getParentElement(focus);
if (parent) {
const tree = widget;
const focusedElements = tree.getFocus();
if (focusedElements.length === 0) {
return;
}
const focus = focusedElements[0];
const parent = tree.getParentElement(focus);
if (parent) {
navigate(widget, widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.setFocus([parent], fakeKeyboardEvent);
tree.reveal(parent);
}
widget.setFocus([parent], fakeKeyboardEvent);
});
}
}
});
@@ -289,202 +338,66 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
when: WorkbenchListFocusContextKey,
primary: KeyCode.RightArrow,
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
const widget = accessor.get(IListService).lastFocusedList;
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList || focused instanceof Table)) {
if (focused instanceof ObjectTree || focused instanceof DataTree) {
// TODO@Joao: instead of doing this here, just delegate to a tree method
const tree = focused;
const focusedElements = tree.getFocus();
if (!widget) {
return;
}
if (focusedElements.length === 0) {
return;
if (widget instanceof ObjectTree || widget instanceof DataTree) {
// TODO@Joao: instead of doing this here, just delegate to a tree method
const focusedElements = widget.getFocus();
if (focusedElements.length === 0) {
return;
}
const focus = focusedElements[0];
if (!widget.expand(focus)) {
const child = widget.getFirstElementChild(focus);
if (child) {
const node = widget.getNode(child);
if (node.visible) {
navigate(widget, widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
widget.setFocus([child], fakeKeyboardEvent);
});
}
}
}
} else if (widget instanceof AsyncDataTree) {
// TODO@Joao: instead of doing this here, just delegate to a tree method
const focusedElements = widget.getFocus();
const focus = focusedElements[0];
if (focusedElements.length === 0) {
return;
}
if (!tree.expand(focus)) {
const child = tree.getFirstElementChild(focus);
const focus = focusedElements[0];
widget.expand(focus).then(didExpand => {
if (focus && !didExpand) {
const child = widget.getFirstElementChild(focus);
if (child) {
const node = tree.getNode(child);
const node = widget.getNode(child);
if (node.visible) {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.setFocus([child], fakeKeyboardEvent);
tree.reveal(child);
}
}
}
} else if (focused instanceof AsyncDataTree) {
// TODO@Joao: instead of doing this here, just delegate to a tree method
const tree = focused;
const focusedElements = tree.getFocus();
if (focusedElements.length === 0) {
return;
}
const focus = focusedElements[0];
tree.expand(focus).then(didExpand => {
if (focus && !didExpand) {
const child = tree.getFirstElementChild(focus);
if (child) {
const node = tree.getNode(child);
if (node.visible) {
navigate(widget, widget => {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.setFocus([child], fakeKeyboardEvent);
tree.reveal(child);
}
widget.setFocus([child], fakeKeyboardEvent);
});
}
}
});
}
}
});
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusPageUp',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.PageUp,
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
focused.focusPreviousPage();
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
focused.focusPreviousPage(fakeKeyboardEvent);
}
// Ensure DOM Focus
ensureDOMFocus(focused);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusPageDown',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.PageDown,
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
focused.focusNextPage();
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
focused.focusNextPage(fakeKeyboardEvent);
}
// Ensure DOM Focus
ensureDOMFocus(focused);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusFirst',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.Home,
handler: accessor => listFocusFirst(accessor)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusFirstChild',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: 0,
handler: accessor => listFocusFirst(accessor, { fromFocused: true })
});
function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
const list = focused;
list.setFocus([0]);
list.reveal(0);
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.focusFirst(fakeKeyboardEvent);
const focus = tree.getFocus();
if (focus.length > 0) {
tree.reveal(focus[0]);
}
}
// Ensure DOM Focus
ensureDOMFocus(focused);
}
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusLast',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: KeyCode.End,
handler: accessor => listFocusLast(accessor)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'list.focusLastChild',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: 0,
handler: accessor => listFocusLast(accessor, { fromFocused: true })
});
function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
const list = focused;
list.setFocus([list.length - 1]);
list.reveal(list.length - 1);
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.focusLast(fakeKeyboardEvent);
const focus = tree.getFocus();
if (focus.length > 0) {
tree.reveal(focus[0]);
}
}
// Ensure DOM Focus
ensureDOMFocus(focused);
}
function focusElement(accessor: ServicesAccessor, retainCurrentFocus: boolean): void {
function selectElement(accessor: ServicesAccessor, retainCurrentFocus: boolean): void {
const focused = accessor.get(IListService).lastFocusedList;
const fakeKeyboardEvent = getSelectionKeyboardEvent('keydown', retainCurrentFocus);
// List
@@ -525,7 +438,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow]
},
handler: (accessor) => {
focusElement(accessor, false);
selectElement(accessor, false);
}
});
@@ -534,7 +447,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
handler: accessor => {
focusElement(accessor, true);
selectElement(accessor, true);
}
});
@@ -654,7 +567,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
}
focusElement(accessor, true);
selectElement(accessor, true);
}
});
@@ -664,43 +577,24 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListHasSelectionOrFocus),
primary: KeyCode.Escape,
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
const widget = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
const list = focused;
list.setSelection([]);
list.setFocus([]);
if (!widget) {
return;
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.setSelection([], fakeKeyboardEvent);
list.setFocus([], fakeKeyboardEvent);
}
const fakeKeyboardEvent = new KeyboardEvent('keydown');
widget.setSelection([], fakeKeyboardEvent);
widget.setFocus([], fakeKeyboardEvent);
widget.setAnchor(undefined);
}
});
CommandsRegistry.registerCommand({
id: 'list.toggleKeyboardNavigation',
handler: (accessor) => {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
const list = focused;
list.toggleKeyboardNavigation();
}
// Tree
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
const tree = focused;
tree.toggleKeyboardNavigation();
}
const widget = accessor.get(IListService).lastFocusedList;
widget?.toggleKeyboardNavigation();
}
});

View File

@@ -343,7 +343,7 @@ export class NewWindowAction extends Action {
}
run(): Promise<void> {
return this.hostService.openWindow();
return this.hostService.openWindow({ remoteAuthority: null });
}
}

View File

@@ -26,6 +26,7 @@ import { IRange } from 'vs/editor/common/core/range';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { TrackedRangeStickiness, IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
export interface IRangeHighlightDecoration {
resource: URI;
@@ -264,6 +265,11 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
}
}
if (editor.getOption(EditorOption.inDiffEditor)) {
// in diff editor
return false;
}
return true;
}

View File

@@ -83,17 +83,21 @@ export abstract class Composite extends Component implements IComposite {
protected actionRunner: IActionRunner | undefined;
private _telemetryService: ITelemetryService;
protected get telemetryService(): ITelemetryService { return this._telemetryService; }
private visible: boolean;
private parent: HTMLElement | undefined;
constructor(
id: string,
private _telemetryService: ITelemetryService,
telemetryService: ITelemetryService,
themeService: IThemeService,
storageService: IStorageService
) {
super(id, themeService, storageService);
this._telemetryService = telemetryService;
this.visible = false;
}
@@ -101,10 +105,6 @@ export abstract class Composite extends Component implements IComposite {
return undefined;
}
protected get telemetryService(): ITelemetryService {
return this._telemetryService;
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
@@ -203,7 +203,7 @@ export abstract class Composite extends Component implements IComposite {
*/
getActionRunner(): IActionRunner {
if (!this.actionRunner) {
this.actionRunner = new ActionRunner();
this.actionRunner = this._register(new ActionRunner());
}
return this.actionRunner;

View File

@@ -958,10 +958,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
return new Dimension(availableWidth, availableHeight);
}
getWorkbenchContainer(): HTMLElement {
return this.parent;
}
toggleZenMode(skipLayout?: boolean, restoring = false): void {
this.state.zenMode.active = !this.state.zenMode.active;
this.state.zenMode.transitionDisposables.clear();

View File

@@ -453,7 +453,7 @@ registerThemingParticipant((theme, collector) => {
outline: 1px dashed;
}
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator:before {
border-left-color: ${outline};
}
@@ -472,7 +472,7 @@ registerThemingParticipant((theme, collector) => {
const focusBorderColor = theme.getColor(focusBorder);
if (focusBorderColor) {
collector.addRule(`
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator:before {
border-left-color: ${focusBorderColor};
}
`);

View File

@@ -76,10 +76,9 @@
}
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before,
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator:before {
content: "";
position: absolute;
top: 0;
z-index: 1;
top: 0;
height: 100%;
@@ -92,12 +91,9 @@
height: 100%;
}
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:focus .active-item-indicator:before {
visibility: hidden; /* don't show active border + focus at the same time, focus takes priority */
}
/* Hides active elements in high contrast mode */
.monaco-workbench.hc-black .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator {
.monaco-workbench.hc-black .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator {
display: none;
}
@@ -105,13 +101,11 @@
border-left: none !important; /* no focus feedback when using mouse */
}
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before,
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before{
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item .active-item-indicator:before{
left: 0;
}
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before,
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item .active-item-indicator:before {
right: 0;
}

View File

@@ -6,7 +6,6 @@
import { localize } from 'vs/nls';
import { IAction, toAction } from 'vs/base/common/actions';
import { illegalArgument } from 'vs/base/common/errors';
import { equals } from 'vs/base/common/arrays';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IBadge } from 'vs/workbench/services/activity/common/activity';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -277,10 +276,8 @@ export class CompositeBar extends Widget implements ICompositeBar {
switch (this.options.orientation) {
case ActionsOrientation.HORIZONTAL:
case ActionsOrientation.HORIZONTAL_REVERSE:
return posX < rect.left;
case ActionsOrientation.VERTICAL:
case ActionsOrientation.VERTICAL_REVERSE:
return posY < rect.top;
}
}
@@ -538,10 +535,8 @@ export class CompositeBar extends Widget implements ICompositeBar {
compositesToShow.length ? compositesToShow.splice(compositesToShow.length - 2, 1) : compositesToShow.pop();
}
const visibleCompositesChange = !equals(compositesToShow, this.visibleComposites);
// Pull out overflow action if there is a composite change so that we can add it to the end later
if (this.compositeOverflowAction && visibleCompositesChange) {
// Remove the overflow action if there are no overflows
if (!overflows && this.compositeOverflowAction) {
compositeSwitcherBar.pull(compositeSwitcherBar.length() - 1);
this.compositeOverflowAction.dispose();
@@ -584,7 +579,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
});
// Add overflow action as needed
if ((visibleCompositesChange && overflows)) {
if ((overflows && !this.compositeOverflowAction)) {
this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => {
if (this.compositeOverflowActionViewItem) {
this.compositeOverflowActionViewItem.showMenu();

View File

@@ -305,8 +305,9 @@ export class ActivityActionViewItem extends BaseActionViewItem {
}
if (clazz) {
this.badge.classList.add(...clazz.split(' '));
this.badgeDisposable.value = toDisposable(() => this.badge.classList.remove(...clazz.split(' ')));
const classNames = clazz.split(' ');
this.badge.classList.add(...classNames);
this.badgeDisposable.value = toDisposable(() => this.badge.classList.remove(...classNames));
}
}

View File

@@ -162,9 +162,9 @@ export class BreadcrumbsControl {
static readonly Payload_RevealAside = {};
static readonly Payload_Pick = {};
static readonly CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false);
static readonly CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
static readonly CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
static readonly CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false, localize('breadcrumbsPossible', "Whether the editor can show breadcrumbs"));
static readonly CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false, localize('breadcrumbsVisible', "Whether breadcrumbs are currently visible"));
static readonly CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false, localize('breadcrumbsActive', "Whether breadcrumbs have focus"));
private readonly _ckBreadcrumbsPossible: IContextKey<boolean>;
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;

View File

@@ -287,7 +287,7 @@ registerEditorContribution(UntitledHintContribution.ID, UntitledHintContribution
// Register Status Actions
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeModeAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode');
registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeModeAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode', undefined, ContextKeyExpr.not('notebookEditorFocused'));
registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEOLAction), 'Change End of Line Sequence');
registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEncodingAction), 'Change File Encoding');

View File

@@ -10,7 +10,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups, UNPIN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -216,7 +216,7 @@ export class JoinAllGroupsAction extends Action {
}
async run(): Promise<void> {
mergeAllGroups(this.editorGroupService);
this.editorGroupService.mergeAllGroups();
}
}

View File

@@ -279,17 +279,6 @@ function registerEditorGroupsLayoutCommand(): void {
});
}
export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
const target = editorGroupService.activeGroup;
for (const group of editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) {
if (group === target) {
continue; // keep target
}
editorGroupService.mergeGroup(group, target);
}
}
function registerDiffEditorCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: GOTO_NEXT_CHANGE,

View File

@@ -1287,7 +1287,18 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Open next active if there are more to show
const nextActiveEditor = this._group.activeEditor;
if (nextActiveEditor) {
const options = EditorOptions.create({ preserveFocus: !focusNext });
const preserveFocus = !focusNext;
let activation: EditorActivation | undefined = undefined;
if (preserveFocus && this.accessor.activeGroup !== this) {
// If we are opening the next editor in an inactive group
// without focussing it, ensure we preserve the editor
// group sizes in case that group is minimized.
// https://github.com/microsoft/vscode/issues/117686
activation = EditorActivation.PRESERVE;
}
const options = EditorOptions.create({ preserveFocus, activation });
// When closing an editor due to an error we can end up in a loop where we continue closing
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
@@ -1610,7 +1621,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Extract active vs. inactive replacements
let activeReplacement: EditorReplacement | undefined;
const inactiveReplacements: EditorReplacement[] = [];
for (let { editor, replacement, options } of editors) {
for (let { editor, replacement, forceReplaceDirty, options } of editors) {
const index = this.getIndexOfEditor(editor);
if (index >= 0) {
const isActiveEditor = this.isActive(editor);
@@ -1625,7 +1636,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
options.inactive = !isActiveEditor;
options.pinned = options.pinned ?? true; // unless specified, prefer to pin upon replace
const editorToReplace = { editor, replacement, options };
const editorToReplace = { editor, replacement, forceReplaceDirty, options };
if (isActiveEditor) {
activeReplacement = editorToReplace;
} else {
@@ -1635,14 +1646,20 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Handle inactive first
for (const { editor, replacement, options } of inactiveReplacements) {
for (const { editor, replacement, forceReplaceDirty, options } of inactiveReplacements) {
// Open inactive editor
await this.doOpenEditor(replacement, options);
// Close replaced inactive editor unless they match
if (!editor.matches(replacement)) {
const closed = await this.doCloseEditorWithDirtyHandling(editor, { preserveFocus: true });
let closed = false;
if (forceReplaceDirty) {
this.doCloseEditor(editor, false);
closed = true;
} else {
closed = await this.doCloseEditorWithDirtyHandling(editor, { preserveFocus: true });
}
if (!closed) {
return; // canceled
}
@@ -1657,7 +1674,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close replaced active editor unless they match
if (!activeReplacement.editor.matches(activeReplacement.replacement)) {
await this.doCloseEditorWithDirtyHandling(activeReplacement.editor, { preserveFocus: true });
if (activeReplacement.forceReplaceDirty) {
this.doCloseEditor(activeReplacement.editor, false);
} else {
await this.doCloseEditorWithDirtyHandling(activeReplacement.editor, { preserveFocus: true });
}
}
await openEditorResult;
@@ -1776,6 +1797,8 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
export interface EditorReplacement {
readonly editor: EditorInput;
readonly replacement: EditorInput;
/** Skips asking the user for confirmation and doesn't save the document. Only use this if you really need to! */
readonly forceReplaceDirty?: boolean;
readonly options?: EditorOptions;
}

View File

@@ -764,6 +764,18 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
return targetView;
}
mergeAllGroups(target = this.activeGroup): IEditorGroupView {
for (const group of this.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) {
if (group === target) {
continue; // keep target
}
this.mergeGroup(group, target);
}
return target;
}
private assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView {
let groupView: IEditorGroupView | undefined;
if (typeof group === 'number') {

View File

@@ -1064,19 +1064,12 @@ export class ChangeModeAction extends Action {
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ITextFileService private readonly textFileService: ITextFileService,
@ICommandService private readonly commandService: ICommandService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super(actionId, actionLabel);
}
async run(event: any, data: ITelemetryData): Promise<void> {
const activeEditorPane = this.editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined;
if (activeEditorPane?.isNotebookEditor) { // TODO@rebornix TODO@jrieken debt: https://github.com/microsoft/vscode/issues/114554
// it's inside notebook editor
return this.commandService.executeCommand('notebook.cell.changeLanguage');
}
const activeTextEditorControl = getCodeEditor(this.editorService.activeTextEditorControl);
if (!activeTextEditorControl) {
await this.quickInputService.pick([{ label: localize('noEditor', "No text editor active at this time") }]);

View File

@@ -101,6 +101,10 @@
margin-right: var(--last-tab-margin-right); /* when tabs wrap, we need a margin away from the absolute positioned editor actions */
}
.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.last-in-row:not(:last-child) {
border-right: 0 !important; /* ensure no border for every last tab in a row except last row (#115046) */
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-right,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact) {
padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */
@@ -113,8 +117,8 @@
flex-shrink: 0;
}
.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.sizing-fit {
flex-grow: 1; /* grow the tabs to fill each row for a more homogeneous look when tabs wrap */
.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.sizing-fit.last-in-row:not(:last-child) {
flex-grow: 1; /* grow the last tab in a row for a more homogeneous look except for last row (#113801) */
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink {

View File

@@ -1482,6 +1482,48 @@ export class TabsTitleControl extends TitleControl {
});
}
// Update the `last-in-row` class on tabs when wrapping
// is enabled (it doesn't do any harm otherwise). This
// class controls additional properties of tab when it is
// the last tab in a row
if (tabsWrapMultiLine) {
// Using a map here to change classes after the for loop is
// crucial for performance because changing the class on a
// tab can result in layouts of the rendering engine.
const tabs = new Map<HTMLElement, boolean /* last in row */>();
let currentTabsPosY: number | undefined = undefined;
let lastTab: HTMLElement | undefined = undefined;
for (const child of tabsContainer.children) {
const tab = child as HTMLElement;
const tabPosY = tab.offsetTop;
// Marks a new or the first row of tabs
if (tabPosY !== currentTabsPosY) {
currentTabsPosY = tabPosY;
if (lastTab) {
tabs.set(lastTab, true); // previous tab must be last in row then
}
}
// Always remember last tab and ensure the
// last-in-row class is not present until
// we know the tab is last
lastTab = tab;
tabs.set(tab, false);
}
// Last tab overally is always last-in-row
if (lastTab) {
tabs.set(lastTab, true);
}
for (const [tab, lastInRow] of tabs) {
tab.classList.toggle('last-in-row', lastInRow);
}
}
return tabsWrapMultiLine;
}

View File

@@ -224,12 +224,11 @@ export abstract class TitleControl extends Themable {
this.updateEditorActionsToolbar(); // Update editor toolbar whenever contributed actions change
}));
const isPrimaryGroup = (group: string) => group === 'navigation' || group === '1_run';
const shouldInlineGroup = (action: SubmenuAction, group: string) => isPrimaryGroup(group) && action.actions.length <= 1;
const shouldInlineGroup = (action: SubmenuAction, group: string) => group === 'navigation' && action.actions.length <= 1;
this.editorToolBarMenuDisposables.add(createAndFillInActionBarActions(
titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary },
isPrimaryGroup, 9, shouldInlineGroup
'navigation', 9, shouldInlineGroup
));
}

View File

@@ -18,6 +18,7 @@ import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor';
import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService';
const $ = dom.$;
const untitledHintSetting = 'workbench.editor.untitled.hint';
@@ -28,13 +29,15 @@ export class UntitledHintContribution implements IEditorContribution {
private toDispose: IDisposable[];
private untitledHintContentWidget: UntitledHintContentWidget | undefined;
private button: FloatingClickWidget | undefined;
private experimentTreatment: 'text' | 'button' | 'hidden' | undefined;
constructor(
private editor: ICodeEditor,
@ICommandService private readonly commandService: ICommandService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IThemeService private readonly themeService: IThemeService
@IThemeService private readonly themeService: IThemeService,
@ITASExperimentService private readonly experimentService: ITASExperimentService
) {
this.toDispose = [];
this.toDispose.push(this.editor.onDidChangeModel(() => this.update()));
@@ -44,13 +47,17 @@ export class UntitledHintContribution implements IEditorContribution {
this.update();
}
}));
this.update();
this.experimentService.getTreatment<'text' | 'button'>('untitledhint').then(treatment => {
this.experimentTreatment = treatment;
this.update();
});
}
private update(): void {
this.untitledHintContentWidget?.dispose();
this.button?.dispose();
const untitledHintMode = this.configurationService.getValue(untitledHintSetting);
const configValue = this.configurationService.getValue<'text' | 'button' | 'hidden' | 'default'>(untitledHintSetting);
const untitledHintMode = configValue === 'default' ? (this.experimentTreatment || 'hidden') : configValue;
const model = this.editor.getModel();
if (model && model.uri.scheme === Schemas.untitled && model.getModeId() === PLAINTEXT_MODE_ID) {

View File

@@ -15,6 +15,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { Codicon } from 'vs/base/common/codicons';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { hash } from 'vs/base/common/hash';
const clearIcon = registerIcon('notifications-clear', Codicon.close, localize('clearIcon', 'Icon for the clear action in notifications.'));
const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll, localize('clearAllIcon', 'Icon for the clear all action in notifications.'));
@@ -145,6 +146,17 @@ export class CopyNotificationMessageAction extends Action {
}
}
interface NotificationActionMetrics {
id: string;
actionLabel: string;
source: string | undefined;
}
type NotificationActionMetricsClassification = {
id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
actionLabel: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
export class NotificationActionRunner extends ActionRunner {
constructor(
@@ -154,8 +166,12 @@ export class NotificationActionRunner extends ActionRunner {
super();
}
protected async runAction(action: IAction, context: INotificationViewItem): Promise<void> {
protected async runAction(action: IAction, context: INotificationViewItem | undefined): Promise<void> {
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: action.id, from: 'message' });
if (context) {
// If the context is not present it is a "global" notification action. Will be captured by other events
this.telemetryService.publicLog2<NotificationActionMetrics, NotificationActionMetricsClassification>('notification:actionExecuted', { id: hash(context.message.original.toString()).toString(), actionLabel: action.label, source: context.sourceId });
}
// Run and make sure to notify on any error again
try {

View File

@@ -7,10 +7,12 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { INotificationViewItem, isNotificationViewItem } from 'vs/workbench/common/notifications';
import { INotificationViewItem, isNotificationViewItem, NotificationsModel } from 'vs/workbench/common/notifications';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { localize } from 'vs/nls';
import { IListService, WorkbenchList } from 'vs/platform/list/browser/listService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NotificationMetrics, NotificationMetricsClassification, notificationToMetrics } from 'vs/workbench/browser/parts/notifications/notificationsTelemetry';
// Center
export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList';
@@ -55,7 +57,7 @@ export interface INotificationsToastController {
hide(): void;
}
export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController): void {
export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController, model: NotificationsModel): void {
function getNotificationFromContext(listService: IListService, context?: unknown): INotificationViewItem | undefined {
if (isNotificationViewItem(context)) {
@@ -85,7 +87,16 @@ export function registerNotificationCommands(center: INotificationsCenterControl
weight: KeybindingWeight.WorkbenchContrib + 50,
when: NotificationsCenterVisibleContext,
primary: KeyCode.Escape,
handler: accessor => center.hide()
handler: accessor => {
const telemetryService = accessor.get(ITelemetryService);
model.notifications.forEach(n => {
if (n.visible) {
telemetryService.publicLog2<NotificationMetrics, NotificationMetricsClassification>('notification:hide', notificationToMetrics(n.message.original, n.sourceId, n.silent));
}
});
center.hide();
}
});
// Toggle Notifications Center
@@ -159,7 +170,15 @@ export function registerNotificationCommands(center: INotificationsCenterControl
});
// Hide Toasts
CommandsRegistry.registerCommand(HIDE_NOTIFICATION_TOAST, accessor => toasts.hide());
CommandsRegistry.registerCommand(HIDE_NOTIFICATION_TOAST, accessor => {
const telemetryService = accessor.get(ITelemetryService);
model.notifications.forEach(n => {
if (n.visible) {
telemetryService.publicLog2<NotificationMetrics, NotificationMetricsClassification>('notification:hide', notificationToMetrics(n.message.original, n.sourceId, n.silent));
}
});
toasts.hide();
});
KeybindingsRegistry.registerKeybindingRule({
id: HIDE_NOTIFICATION_TOAST,

View File

@@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { INotificationService, NotificationMessage } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { hash } from 'vs/base/common/hash';
export interface NotificationMetrics {
id: string;
silent: boolean;
source?: string;
}
export type NotificationMetricsClassification = {
id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
silent: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
export function notificationToMetrics(message: NotificationMessage, source: string | undefined, silent: boolean): NotificationMetrics {
return {
id: hash(message.toString()).toString(),
silent,
source: source || 'core'
};
}
export class NotificationsTelemetry extends Disposable implements IWorkbenchContribution {
constructor(
@ITelemetryService private readonly telemetryService: ITelemetryService,
@INotificationService private readonly notificationService: INotificationService
) {
super();
this.registerListeners();
}
private registerListeners(): void {
this._register(this.notificationService.onDidAddNotification(notification => {
const source = notification.source && typeof notification.source !== 'string' ? notification.source.id : notification.source;
this.telemetryService.publicLog2<NotificationMetrics, NotificationMetricsClassification>('notification:show', notificationToMetrics(notification.message, source, !!notification.silent));
}));
this._register(this.notificationService.onDidRemoveNotification(notification => {
const source = notification.source && typeof notification.source !== 'string' ? notification.source.id : notification.source;
this.telemetryService.publicLog2<NotificationMetrics, NotificationMetricsClassification>('notification:close', notificationToMetrics(notification.message, source, !!notification.silent));
}));
}
}

View File

@@ -178,15 +178,17 @@
}
/* Rotate icons when panel is on right */
.monaco-workbench .part.panel.right .title-actions .codicon-split-horizontal,
.monaco-workbench .part.panel.right .title-actions .codicon-panel-maximize,
.monaco-workbench .part.panel.right .title-actions .codicon-panel-restore {
.monaco-workbench .part.panel.right .title-actions .codicon-split-horizontal::before,
.monaco-workbench .part.panel.right .title-actions .codicon-panel-maximize::before,
.monaco-workbench .part.panel.right .title-actions .codicon-panel-restore::before {
display: inline-block;
transform: rotate(-90deg);
}
/* Rotate icons when panel is on left */
.monaco-workbench .part.panel.left .title-actions .codicon-split-horizontal,
.monaco-workbench .part.panel.left .title-actions .codicon-panel-maximize,
.monaco-workbench .part.panel.left .title-actions .codicon-panel-restore {
.monaco-workbench .part.panel.left .title-actions .codicon-split-horizontal::before,
.monaco-workbench .part.panel.left .title-actions .codicon-panel-maximize::before,
.monaco-workbench .part.panel.left .title-actions .codicon-panel-restore::before {
display: inline-block;
transform: rotate(90deg);
}

View File

@@ -41,6 +41,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { ILogService } from 'vs/platform/log/common/log';
import { Cookie } from 'vs/server/common/cookie';
export type IOpenRecentAction = IAction & { uri: URI, remoteAuthority?: string };
export abstract class MenubarControl extends Disposable {
protected keys = [
@@ -160,7 +162,7 @@ export abstract class MenubarControl extends Disposable {
this.updateMenubar();
}
protected getOpenRecentActions(): (Separator | IAction & { uri: URI })[] {
protected getOpenRecentActions(): (Separator | IOpenRecentAction)[] {
if (!this.recentlyOpened) {
return [];
}
@@ -226,12 +228,13 @@ export abstract class MenubarControl extends Disposable {
}
}
private createOpenRecentMenuAction(recent: IRecent): IAction & { uri: URI } {
private createOpenRecentMenuAction(recent: IRecent): IOpenRecentAction {
let label: string;
let uri: URI;
let commandId: string;
let openable: IWindowOpenable;
const remoteAuthority = recent.remoteAuthority;
if (isRecentFolder(recent)) {
uri = recent.folderUri;
@@ -254,11 +257,12 @@ export abstract class MenubarControl extends Disposable {
const openInNewWindow = event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
return this.hostService.openWindow([openable], {
forceNewWindow: openInNewWindow
forceNewWindow: openInNewWindow,
remoteAuthority
});
});
return Object.assign(ret, { uri });
return Object.assign(ret, { uri, remoteAuthority });
}
private notifyUserOfCustomMenubarAccessibility(): void {
@@ -415,7 +419,6 @@ export class CustomMenubarControl extends MenubarControl {
.monaco-workbench .menubar > .menubar-menu-button.open,
.monaco-workbench .menubar > .menubar-menu-button:focus,
.monaco-workbench .menubar > .menubar-menu-button:hover {
outline-offset: -1px;
outline-color: ${menubarSelectedBorderColor};
}
`);
@@ -594,6 +597,7 @@ export class CustomMenubarControl extends MenubarControl {
const updateActions = (menu: IMenu, target: IAction[], topLevelTitle: string) => {
target.splice(0);
let groups = menu.getActions();
for (let group of groups) {
const [, actions] = group;
@@ -622,7 +626,10 @@ export class CustomMenubarControl extends MenubarControl {
const submenuActions: SubmenuAction[] = [];
updateActions(submenu, submenuActions, topLevelTitle);
target.push(new SubmenuAction(action.id, mnemonicMenuLabel(title), submenuActions));
if (submenuActions.length > 0) {
target.push(new SubmenuAction(action.id, mnemonicMenuLabel(title), submenuActions));
}
} else {
const newAction = new Action(action.id, mnemonicMenuLabel(title), action.class, action.enabled, () => this.commandService.executeCommand(action.id));
newAction.tooltip = action.tooltip;

View File

@@ -133,7 +133,7 @@ export class TitlebarPart extends Part implements ITitleService {
this.titleUpdater.schedule();
}
if (this.titleBarStyle !== 'native') {
if (this.titleBarStyle !== 'native' && (!isMacintosh || isWeb)) {
if (event.affectsConfiguration('window.menuBarVisibility')) {
if (this.currentMenubarVisibility === 'compact') {
this.uninstallMenubar();

View File

@@ -205,11 +205,11 @@ export class TreeView extends Disposable implements ITreeView {
) {
super();
this.root = new Root();
this.collapseAllContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableCollapseAll`, false);
this.collapseAllContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableCollapseAll`, false, localize('treeView.enableCollapseAll', "Whether the the tree view with id {0} enables collapse all.", this.id));
this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService);
this.collapseAllToggleContextKey = new RawContextKey<boolean>(`treeView.${this.id}.toggleCollapseAll`, false);
this.collapseAllToggleContextKey = new RawContextKey<boolean>(`treeView.${this.id}.toggleCollapseAll`, false, localize('treeView.toggleCollapseAll', "Whether collapse all is toggled for the tree view with id {0}.", this.id));
this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService);
this.refreshContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableRefresh`, false);
this.refreshContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableRefresh`, false, localize('treeView.enableRefresh', "Whether the tree view with id {0} enables refresh.", this.id));
this.refreshContext = this.refreshContextKey.bindTo(contextKeyService);
this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/));
@@ -853,7 +853,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
this._hoverDelegate = {
showHover: (options: IHoverDelegateOptions): IDisposable | undefined => {
return this.hoverService.showHover(options);
}
},
delay: <number>this.configurationService.getValue('workbench.hover.delay')
};
}
@@ -1133,7 +1134,7 @@ class TreeMenus extends Disposable implements IDisposable {
const primary: IAction[] = [];
const secondary: IAction[] = [];
const result = { primary, secondary };
createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g));
createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, 'inline');
menu.dispose();
return result;

View File

@@ -52,13 +52,13 @@ export class BrowserWindow extends Disposable {
this._register(addDisposableListener(viewport, EventType.RESIZE, () => this.onWindowResize()));
// Prevent the back/forward gestures in macOS
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.WHEEL, e => e.preventDefault(), { passive: false }));
this._register(addDisposableListener(this.layoutService.container, EventType.WHEEL, e => e.preventDefault(), { passive: false }));
// Prevent native context menus in web
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.CONTEXT_MENU, e => EventHelper.stop(e, true)));
this._register(addDisposableListener(this.layoutService.container, EventType.CONTEXT_MENU, e => EventHelper.stop(e, true)));
// Prevent default navigation on drop
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.DROP, e => EventHelper.stop(e, true)));
this._register(addDisposableListener(this.layoutService.container, EventType.DROP, e => EventHelper.stop(e, true)));
// Fullscreen (Browser)
[EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE].forEach(event => {
@@ -153,7 +153,7 @@ export class BrowserWindow extends Disposable {
scheme: Schemas.userData,
priority: true,
formatting: {
label: '${scheme}:${path}',
label: '(Settings) ${path}',
separator: '/',
}
});

View File

@@ -23,7 +23,7 @@ import { isStandalone } from 'vs/base/browser/browser';
enum: ['default', 'large'],
enumDescriptions: [
localize('workbench.editor.titleScrollbarSizing.default', "The default size."),
localize('workbench.editor.titleScrollbarSizing.large', "Increases the size, so it can be grabbed more easily with the mouse")
localize('workbench.editor.titleScrollbarSizing.large', "Increases the size, so it can be grabbed more easily with the mouse.")
],
description: localize('tabScrollbarHeight', "Controls the height of the scrollbars used for tabs and breadcrumbs in the editor title area."),
default: 'default',
@@ -40,7 +40,7 @@ import { isStandalone } from 'vs/base/browser/browser';
},
'workbench.editor.scrollToSwitchTabs': {
'type': 'boolean',
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behaviour for that duration. This value is ignored when `#workbench.editor.showTabs#` is disabled."),
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when `#workbench.editor.showTabs#` is disabled."),
'default': false
},
'workbench.editor.highlightModifiedTabs': {
@@ -51,12 +51,12 @@ import { isStandalone } from 'vs/base/browser/browser';
'workbench.editor.decorations.badges': {
'type': 'boolean',
'markdownDescription': localize('decorations.badges', "Controls whether editor file decorations should use badges."),
'default': false
'default': true
},
'workbench.editor.decorations.colors': {
'type': 'boolean',
'markdownDescription': localize('decorations.colors', "Controls whether editor file decorations should use colors."),
'default': false
'default': true
},
'workbench.editor.labelFormat': {
'type': 'string',
@@ -88,8 +88,8 @@ import { isStandalone } from 'vs/base/browser/browser';
},
'workbench.editor.untitled.hint': {
'type': 'string',
'enum': ['text', 'button', 'hidden'],
'default': 'hidden',
'enum': ['text', 'button', 'hidden', 'default'],
'default': 'default',
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'untitledHint' }, "Controls if the untitled hint should be inline text in the editor or a floating button or hidden.")
},
'workbench.editor.tabCloseButton': {
@@ -174,7 +174,7 @@ import { isStandalone } from 'vs/base/browser/browser';
'type': 'string',
'enum': ['right', 'down'],
'default': 'right',
'markdownDescription': localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.")
'markdownDescription': localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (for example, from the Explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.")
},
'workbench.editor.closeEmptyGroups': {
'type': 'boolean',
@@ -325,6 +325,13 @@ import { isStandalone } from 'vs/base/browser/browser';
'description': localize('settings.editor.desc', "Determines which settings editor to use by default."),
'default': 'ui',
'scope': ConfigurationScope.WINDOW
},
'workbench.hover.delay': {
'type': 'number',
'description': localize('workbench.hover.delay', "Controls the delay in milliseconds after which the hover is shown for workbench items (ex. some extension provided tree view items). Already visible items may require a refresh before reflecting this setting change."),
// Testing has indicated that on Windows and Linux 500 ms matches the native hovers most closely.
// On Mac, the delay is 1500.
'default': isMacintosh ? 1500 : 500
}
}
});

View File

@@ -26,6 +26,7 @@ import { NotificationService } from 'vs/workbench/services/notification/common/n
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/notificationsAlerts';
import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus';
import { NotificationsTelemetry } from 'vs/workbench/browser/parts/notifications/notificationsTelemetry';
import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
import { setARIAContainer } from 'vs/base/browser/ui/aria/aria';
@@ -378,6 +379,7 @@ export class Workbench extends Layout {
const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.container, notificationService.model));
this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model));
const notificationsStatus = instantiationService.createInstance(NotificationsStatus, notificationService.model);
this._register(instantiationService.createInstance(NotificationsTelemetry));
// Visibility
this._register(notificationsCenter.onDidChangeVisibility(() => {
@@ -390,7 +392,7 @@ export class Workbench extends Layout {
}));
// Register Commands
registerNotificationCommands(notificationsCenter, notificationsToasts);
registerNotificationCommands(notificationsCenter, notificationsToasts, notificationService.model);
// Register with Layout
this.registerNotifications({