From 1ca765bab789ee0ae9073ab38911f43c9a224120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20L=C3=B6rwald?= <10850250+stefanloerwald@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:38:48 +0200 Subject: [PATCH 01/35] Fixes #125303 This changes the placement of a div with this class from `top: auto` to `top: 0`, which will avoid placing this div vertically below a sibling div. --- src/vs/base/browser/ui/aria/aria.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/aria/aria.css b/src/vs/base/browser/ui/aria/aria.css index fdcbb34c7d7..af06e896bd5 100644 --- a/src/vs/base/browser/ui/aria/aria.css +++ b/src/vs/base/browser/ui/aria/aria.css @@ -6,4 +6,5 @@ .monaco-aria-container { position: absolute; /* try to hide from window but not from screen readers */ left:-999em; -} \ No newline at end of file + top: 0; /* avoid being placed underneath a sibling element */ +} From 6913aad2da5b827d96f693d5fc5d1be801acfced Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 8 Jun 2021 05:45:40 -0700 Subject: [PATCH 02/35] Re-enable environment variable collection tests Fixes #119826 --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index da2da9499fc..d2298c1dabe 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -668,8 +668,7 @@ import { assertNoRpc } from '../utils'; }); }); - // https://github.com/microsoft/vscode/issues/119826 - suite.skip('environmentVariableCollection', () => { + suite('environmentVariableCollection', () => { test('should have collection variables apply to terminals immediately after setting', (done) => { // Text to match on before passing the test const expectedText = [ From 3c62b7834ad6577e405b43ebc9c9f7d3e2d2e9a9 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 9 Jun 2021 10:27:50 -0400 Subject: [PATCH 03/35] Fix #124386 --- .../workbench/services/editor/common/editorOverrideService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/services/editor/common/editorOverrideService.ts b/src/vs/workbench/services/editor/common/editorOverrideService.ts index c2bfe9712ca..41f0725def5 100644 --- a/src/vs/workbench/services/editor/common/editorOverrideService.ts +++ b/src/vs/workbench/services/editor/common/editorOverrideService.ts @@ -171,6 +171,8 @@ export function globMatchesResource(globPattern: string | glob.IRelativePattern, const excludedSchemes = new Set([ Schemas.extension, Schemas.webviewPanel, + Schemas.vscodeWorkspaceTrust, + Schemas.walkThrough ]); // We want to say that the above schemes match no glob patterns if (excludedSchemes.has(resource.scheme)) { From 4168941deec97e4416493daf844e516740c716b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Jun 2021 16:36:59 +0200 Subject: [PATCH 04/35] remove `domEvent` related to #123487 --- src/vs/base/browser/dom.ts | 27 ++++++------ src/vs/base/browser/event.ts | 21 ++-------- src/vs/base/browser/markdownRenderer.ts | 6 ++- src/vs/base/browser/ui/dialog/dialog.ts | 13 +++--- src/vs/base/browser/ui/inputbox/inputBox.ts | 7 ++-- src/vs/base/browser/ui/list/listView.ts | 42 +++++++++---------- src/vs/base/browser/ui/list/listWidget.ts | 25 +++++------ .../browser/ui/selectBox/selectBoxCustom.ts | 8 ++-- src/vs/base/browser/ui/splitview/paneview.ts | 29 +++++++------ src/vs/base/browser/ui/splitview/splitview.ts | 7 ++-- src/vs/base/browser/ui/tree/abstractTree.ts | 19 +++++---- .../parameterHints/parameterHintsWidget.ts | 13 +++--- .../browser/menuEntryActionViewItem.ts | 7 ++-- .../contextview/browser/contextMenuHandler.ts | 13 +++--- src/vs/platform/opener/browser/link.ts | 5 ++- .../browser/parts/compositeBarActions.ts | 9 ++-- .../debug/browser/debugEditorContribution.ts | 9 ++-- .../abstractRuntimeExtensionsEditor.ts | 7 +--- .../extensions/browser/extensionEditor.ts | 4 +- .../extensions/browser/extensionsList.ts | 6 +-- .../extensions/browser/extensionsViewer.ts | 4 +- .../files/browser/views/explorerViewer.ts | 5 +-- .../markers/browser/markersTreeViewer.ts | 6 +-- .../contrib/markers/browser/markersView.ts | 3 +- .../browser/view/renderers/cellDnd.ts | 10 ++--- .../browser/view/renderers/cellRenderer.ts | 3 +- .../walkThrough/browser/walkThroughPart.ts | 5 +-- .../host/browser/browserHostService.ts | 5 ++- 28 files changed, 146 insertions(+), 172 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index a5d014dc8a8..041fbc8a0ad 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as browser from 'vs/base/browser/browser'; -import { domEvent } from 'vs/base/browser/event'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { TimeoutTimer } from 'vs/base/common/async'; @@ -975,8 +974,8 @@ class FocusTracker extends Disposable implements IFocusTracker { } }; - this._register(domEvent(element, EventType.FOCUS, true)(onFocus)); - this._register(domEvent(element, EventType.BLUR, true)(onBlur)); + this._register(addDisposableListener(element, EventType.FOCUS, onFocus, true)); + this._register(addDisposableListener(element, EventType.BLUR, onBlur, true)); } refreshState() { @@ -1480,7 +1479,7 @@ export class ModifierKeyEmitter extends Emitter { metaKey: false }; - this._subscriptions.add(domEvent(window, 'keydown', true)(e => { + this._subscriptions.add(addDisposableListener(window, 'keydown', e => { if (e.defaultPrevented) { return; } @@ -1515,9 +1514,9 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus.event = e; this.fire(this._keyStatus); } - })); + }, true)); - this._subscriptions.add(domEvent(window, 'keyup', true)(e => { + this._subscriptions.add(addDisposableListener(window, 'keyup', e => { if (e.defaultPrevented) { return; } @@ -1547,23 +1546,23 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus.event = e; this.fire(this._keyStatus); } - })); + }, true)); - this._subscriptions.add(domEvent(document.body, 'mousedown', true)(e => { + this._subscriptions.add(addDisposableListener(document.body, 'mousedown', () => { this._keyStatus.lastKeyPressed = undefined; - })); + }, true)); - this._subscriptions.add(domEvent(document.body, 'mouseup', true)(e => { + this._subscriptions.add(addDisposableListener(document.body, 'mouseup', () => { this._keyStatus.lastKeyPressed = undefined; - })); + }, true)); - this._subscriptions.add(domEvent(document.body, 'mousemove', true)(e => { + this._subscriptions.add(addDisposableListener(document.body, 'mousemove', e => { if (e.buttons) { this._keyStatus.lastKeyPressed = undefined; } - })); + }, true)); - this._subscriptions.add(domEvent(window, 'blur')(e => { + this._subscriptions.add(addDisposableListener(window, 'blur', () => { this.resetKeyStatus(); })); } diff --git a/src/vs/base/browser/event.ts b/src/vs/base/browser/event.ts index a1214db777d..a9d91853fe8 100644 --- a/src/vs/base/browser/event.ts +++ b/src/vs/base/browser/event.ts @@ -14,24 +14,7 @@ export interface IDomEvent { (element: EventHandler, type: string, useCapture?: boolean): BaseEvent; } -/** - * @deprecated Use `DomEmitter` instead - */ -export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?: boolean) => { - const fn = (e: Event) => emitter.fire(e); - const emitter = new Emitter({ - onFirstListenerAdd: () => { - element.addEventListener(type, fn, useCapture); - }, - onLastListenerRemove: () => { - element.removeEventListener(type, fn, useCapture); - } - }); - - return emitter.event; -}; - -export interface DOMEventMap extends HTMLElementEventMap { +export interface DOMEventMap extends HTMLElementEventMap, DocumentEventMap { '-monaco-gesturetap': GestureEvent; '-monaco-gesturechange': GestureEvent; '-monaco-gesturestart': GestureEvent; @@ -47,6 +30,8 @@ export class DomEmitter implements IDisposable { return this.emitter.event; } + constructor(element: Document, type: DocumentEventMap, useCapture?: boolean); + constructor(element: EventHandler, type: K, useCapture?: boolean); constructor(element: EventHandler, type: K, useCapture?: boolean) { const fn = (e: Event) => this.emitter.fire(e as DOMEventMap[K]); this.emitter = new Emitter({ diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 78451aa3776..b5aca5ee4d5 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -20,7 +20,7 @@ import { resolvePath } from 'vs/base/common/resources'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Event } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; +import { DomEmitter } from 'vs/base/browser/event'; export interface MarkedOptions extends marked.MarkedOptions { baseUrl?: never; @@ -186,7 +186,9 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende } if (options.actionHandler) { - options.actionHandler.disposeables.add(Event.any(domEvent(element, 'click'), domEvent(element, 'auxclick'))(e => { + const onClick = options.actionHandler.disposeables.add(new DomEmitter(element, 'click')); + const onAuxClick = options.actionHandler.disposeables.add(new DomEmitter(element, 'auxclick')); + options.actionHandler.disposeables.add(Event.any(onClick.event, onAuxClick.event)(e => { const mouseEvent = new StandardMouseEvent(e); if (!mouseEvent.leftButton && !mouseEvent.middleButton) { return; diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 84757ba8cc8..0ebf7934c83 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -7,7 +7,6 @@ import 'vs/css!./dialog'; import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { $, hide, show, EventHelper, clearNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom'; -import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; @@ -220,7 +219,7 @@ export class Dialog extends Disposable { }); // Handle keyboard events gloably: Tab, Arrow-Left/Right - this._register(domEvent(window, 'keydown', true)((e: KeyboardEvent) => { + this._register(addDisposableListener(window, 'keydown', e => { const evt = new StandardKeyboardEvent(e); if (evt.equals(KeyMod.Alt)) { @@ -321,9 +320,9 @@ export class Dialog extends Disposable { } else if (this.options.keyEventProcessor) { this.options.keyEventProcessor(evt); } - })); + }, true)); - this._register(domEvent(window, 'keyup', true)((e: KeyboardEvent) => { + this._register(addDisposableListener(window, 'keyup', e => { EventHelper.stop(e, true); const evt = new StandardKeyboardEvent(e); @@ -333,10 +332,10 @@ export class Dialog extends Disposable { checkboxChecked: this.checkbox ? this.checkbox.checked : undefined }); } - })); + }, true)); // Detect focus out - this._register(domEvent(this.element, 'focusout', false)((e: FocusEvent) => { + this._register(addDisposableListener(this.element, 'focusout', e => { if (!!e.relatedTarget && !!this.element) { if (!isAncestor(e.relatedTarget as HTMLElement, this.element)) { this.focusToReturn = e.relatedTarget as HTMLElement; @@ -347,7 +346,7 @@ export class Dialog extends Disposable { } } } - })); + }, false)); const spinModifierClassName = 'codicon-modifier-spin'; diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 62638456615..34da4ee8a69 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -21,7 +21,7 @@ import { HistoryNavigator } from 'vs/base/common/history'; import { IHistoryNavigationWidget } from 'vs/base/browser/history'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { domEvent } from 'vs/base/browser/event'; +import { DomEmitter } from 'vs/base/browser/event'; const $ = dom.$; @@ -189,13 +189,14 @@ export class InputBox extends Widget { // from ScrollableElement to DOM this._register(this.scrollableElement.onScroll(e => this.input.scrollTop = e.scrollTop)); - const onSelectionChange = Event.filter(domEvent(document, 'selectionchange'), () => { + const onSelectionChange = this._register(new DomEmitter(document, 'selectionchange')); + const onAnchoredSelectionChange = Event.filter(onSelectionChange.event, () => { const selection = document.getSelection(); return selection?.anchorNode === wrapper; }); // from DOM to ScrollableElement - this._register(onSelectionChange(this.updateScrollDimensions, this)); + this._register(onAnchoredSelectionChange(this.updateScrollDimensions, this)); this._register(this.onDidHeightChange(this.updateScrollDimensions, this)); } else { this.input.type = this.options.type || 'text'; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 580812f1197..426d4acdc78 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -7,7 +7,6 @@ import { getOrDefault } from 'vs/base/common/objects'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { Event, Emitter } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions, Scrollable } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; @@ -21,7 +20,8 @@ import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd' import { disposableTimeout, Delayer } from 'vs/base/common/async'; import { isFirefox } from 'vs/base/browser/browser'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { $, animate, getContentHeight, getContentWidth, getTopLeftOffset, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { $, addDisposableListener, animate, getContentHeight, getContentWidth, getTopLeftOffset, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { DomEmitter } from 'vs/base/browser/event'; interface IItem { readonly id: string; @@ -339,17 +339,16 @@ export class ListView implements ISpliceable, IDisposable { container.appendChild(this.domNode); this.scrollableElement.onScroll(this.onScroll, this, this.disposables); - domEvent(this.rowsContainer, TouchEventType.Change)(e => this.onTouchChange(e as GestureEvent), this, this.disposables); + this.disposables.add(addDisposableListener(this.rowsContainer, TouchEventType.Change, e => this.onTouchChange(e as GestureEvent))); // Prevent the monaco-scrollable-element from scrolling // https://github.com/microsoft/vscode/issues/44181 - domEvent(this.scrollableElement.getDomNode(), 'scroll') - (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); + this.disposables.add(addDisposableListener(this.scrollableElement.getDomNode(), 'scroll', e => (e.target as HTMLElement).scrollTop = 0)); - Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e))(this.onDragOver, this, this.disposables); - Event.map(domEvent(this.domNode, 'drop'), e => this.toDragEvent(e))(this.onDrop, this, this.disposables); - domEvent(this.domNode, 'dragleave')(this.onDragLeave, this, this.disposables); - domEvent(window, 'dragend')(this.onDragEnd, this, this.disposables); + this.disposables.add(addDisposableListener(this.domNode, 'dragover', e => this.onDragOver(this.toDragEvent(e)))); + this.disposables.add(addDisposableListener(this.domNode, 'drop', e => this.onDrop(this.toDragEvent(e)))); + this.disposables.add(addDisposableListener(this.domNode, 'dragleave', _ => this.onDragLeave())); + this.disposables.add(addDisposableListener(this.domNode, 'dragend', e => this.onDragEnd(e))); this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); this.setRowHeight = getOrDefault(options, o => o.setRowHeight, DefaultOptions.setRowHeight); @@ -775,8 +774,7 @@ export class ListView implements ISpliceable, IDisposable { item.row.domNode.draggable = !!uri; if (uri) { - const onDragStart = domEvent(item.row.domNode, 'dragstart'); - item.dragStartDisposable = onDragStart(event => this.onDragStart(item.element, uri, event)); + item.dragStartDisposable = addDisposableListener(item.row.domNode, 'dragstart', event => this.onDragStart(item.element, uri, event)); } if (this.horizontalScrolling) { @@ -890,17 +888,17 @@ export class ListView implements ISpliceable, IDisposable { // Events - @memoize get onMouseClick(): Event> { return Event.map(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)); } - @memoize get onMouseDblClick(): Event> { return Event.map(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)); } - @memoize get onMouseMiddleClick(): Event> { return Event.filter(Event.map(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } - @memoize get onMouseUp(): Event> { return Event.map(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)); } - @memoize get onMouseDown(): Event> { return Event.map(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)); } - @memoize get onMouseOver(): Event> { return Event.map(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)); } - @memoize get onMouseMove(): Event> { return Event.map(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)); } - @memoize get onMouseOut(): Event> { return Event.map(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); } - @memoize get onContextMenu(): Event | IListGestureEvent> { return Event.any(Event.map(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)), Event.map(domEvent(this.domNode, TouchEventType.Contextmenu) as Event, e => this.toGestureEvent(e))); } - @memoize get onTouchStart(): Event> { return Event.map(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)); } - @memoize get onTap(): Event> { return Event.map(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e as GestureEvent)); } + @memoize get onMouseClick(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'click')).event, e => this.toMouseEvent(e)); } + @memoize get onMouseDblClick(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'dblclick')).event, e => this.toMouseEvent(e)); } + @memoize get onMouseMiddleClick(): Event> { return Event.filter(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'auxclick')).event, e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } + @memoize get onMouseUp(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseup')).event, e => this.toMouseEvent(e)); } + @memoize get onMouseDown(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousedown')).event, e => this.toMouseEvent(e)); } + @memoize get onMouseOver(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseover')).event, e => this.toMouseEvent(e)); } + @memoize get onMouseMove(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousemove')).event, e => this.toMouseEvent(e)); } + @memoize get onMouseOut(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseout')).event, e => this.toMouseEvent(e)); } + @memoize get onContextMenu(): Event | IListGestureEvent> { return Event.any(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'contextmenu')).event, e => this.toMouseEvent(e)), Event.map(this.disposables.add(new DomEmitter(this.domNode, TouchEventType.Contextmenu)).event as Event, e => this.toGestureEvent(e))); } + @memoize get onTouchStart(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'touchstart')).event, e => this.toTouchEvent(e)); } + @memoize get onTap(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.rowsContainer, TouchEventType.Tap)).event, e => this.toGestureEvent(e as GestureEvent)); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 93906052348..d4a6bda722c 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -13,7 +13,7 @@ import { Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; -import { domEvent, stopEvent } from 'vs/base/browser/event'; +import { DomEmitter, stopEvent } from 'vs/base/browser/event'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list'; import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView'; import { Color } from 'vs/base/common/color'; @@ -257,7 +257,7 @@ class KeyboardController implements IDisposable { ) { const multipleSelectionSupport = options.multipleSelectionSupport !== false; - const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) + const onKeyDown = Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -394,7 +394,7 @@ class TypeLabelController implements IDisposable { return; } - const onChar = Event.chain(domEvent(this.view.domNode, 'keydown')) + const onChar = Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) .filter(e => !isInputElement(e.target as HTMLElement)) .filter(() => this.automaticKeyboardNavigation || this.triggered) .map(event => new StandardKeyboardEvent(event)) @@ -477,7 +477,7 @@ class DOMFocusController implements IDisposable { private list: List, private view: ListView ) { - const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) + const onKeyDown = Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -1184,14 +1184,14 @@ export class List implements ISpliceable, IThemable, IDisposable { @memoize get onContextMenu(): Event> { let didJustPressContextMenuKey = false; - const fromKeyDown = Event.chain(domEvent(this.view.domNode, 'keydown')) + const fromKeyDown = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) .map(e => new StandardKeyboardEvent(e)) .filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .map(stopEvent) .filter(() => false) .event as Event; - const fromKeyUp = Event.chain(domEvent(this.view.domNode, 'keyup')) + const fromKeyUp = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event) .forEach(() => didJustPressContextMenuKey = false) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) @@ -1213,12 +1213,12 @@ export class List implements ISpliceable, IThemable, IDisposable { return Event.any>(fromKeyDown, fromKeyUp, fromMouse); } - get onKeyDown(): Event { return domEvent(this.view.domNode, 'keydown'); } - get onKeyUp(): Event { return domEvent(this.view.domNode, 'keyup'); } - get onKeyPress(): Event { return domEvent(this.view.domNode, 'keypress'); } + @memoize get onKeyDown(): Event { return this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event; } + @memoize get onKeyUp(): Event { return this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event; } + @memoize get onKeyPress(): Event { return this.disposables.add(new DomEmitter(this.view.domNode, 'keypress')).event; } - readonly onDidFocus: Event; - readonly onDidBlur: Event; + @memoize get onDidFocus(): Event { return Event.signal(this.disposables.add(new DomEmitter(this.view.domNode, 'focus', true)).event); } + @memoize get onDidBlur(): Event { return Event.signal(this.disposables.add(new DomEmitter(this.view.domNode, 'blur', true)).event); } private readonly _onDidDispose = new Emitter(); readonly onDidDispose: Event = this._onDidDispose.event; @@ -1277,9 +1277,6 @@ export class List implements ISpliceable, IThemable, IDisposable { this.disposables.add(this.view); this.disposables.add(this._onDidDispose); - this.onDidFocus = Event.map(domEvent(this.view.domNode, 'focus', true), () => null!); - this.onDidBlur = Event.map(domEvent(this.view.domNode, 'blur', true), () => null!); - this.disposables.add(new DOMFocusController(this, this.view)); if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) { diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index a2447f6db79..c2347525d84 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -14,7 +14,7 @@ import * as arrays from 'vs/base/common/arrays'; import { IContextViewProvider, AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListEvent } from 'vs/base/browser/ui/list/list'; -import { domEvent } from 'vs/base/browser/event'; +import { DomEmitter } from 'vs/base/browser/event'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; @@ -736,7 +736,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } // SetUp list keyboard controller - control navigation, disabled items, focus - const onSelectDropDownKeyDown = Event.chain(domEvent(this.selectDropDownListContainer, 'keydown')) + const onKeyDown = this._register(new DomEmitter(this.selectDropDownListContainer, 'keydown')); + const onSelectDropDownKeyDown = Event.chain(onKeyDown.event) .filter(() => this.selectList.length > 0) .map(e => new StandardKeyboardEvent(e)); @@ -752,7 +753,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // SetUp list mouse controller - control navigation, disabled items, focus - this._register(Event.chain(domEvent(this.selectList.getHTMLElement(), 'mouseup')) + const onMouseUp = this._register(new DomEmitter(this.selectList.getHTMLElement(), 'mouseup')); + this._register(Event.chain(onMouseUp.event) .filter(() => this.selectList.length > 0) .on(e => this.onMouseUp(e), this)); diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index a84c6f5837e..92bc2683838 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -6,10 +6,9 @@ import 'vs/css!./paneview'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { $, append, trackFocus, EventHelper, clearNode } from 'vs/base/browser/dom'; +import { $, append, trackFocus, EventHelper, clearNode, addDisposableListener } from 'vs/base/browser/dom'; import { Color, RGBA } from 'vs/base/common/color'; import { SplitView, IView } from './splitview'; import { isFirefox } from 'vs/base/browser/browser'; @@ -17,6 +16,7 @@ import { DataTransfers } from 'vs/base/browser/dnd'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { localize } from 'vs/nls'; import { ScrollEvent } from 'vs/base/common/scrollable'; +import { DomEmitter } from 'vs/base/browser/event'; export interface IPaneOptions { minimumBodySize?: number; @@ -220,8 +220,8 @@ export abstract class Pane extends Disposable implements IView { this.updateHeader(); - - const onHeaderKeyDown = Event.chain(domEvent(this.header, 'keydown')) + const onKeyDown = this._register(new DomEmitter(this.header, 'keydown')); + const onHeaderKeyDown = Event.chain(onKeyDown.event) .map(e => new StandardKeyboardEvent(e)); this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) @@ -233,12 +233,11 @@ export abstract class Pane extends Disposable implements IView { this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow) .event(() => this.setExpanded(true), null)); - this._register(domEvent(this.header, 'click') - (e => { - if (!e.defaultPrevented) { - this.setExpanded(!this.isExpanded()); - } - }, null)); + this._register(addDisposableListener(this.header, 'click', e => { + if (!e.defaultPrevented) { + this.setExpanded(!this.isExpanded()); + } + })); this.body = append(this.element, $('.pane-body')); this.renderBody(this.body); @@ -308,11 +307,11 @@ class PaneDraggable extends Disposable { super(); pane.draggableElement.draggable = true; - this._register(domEvent(pane.draggableElement, 'dragstart')(this.onDragStart, this)); - this._register(domEvent(pane.dropTargetElement, 'dragenter')(this.onDragEnter, this)); - this._register(domEvent(pane.dropTargetElement, 'dragleave')(this.onDragLeave, this)); - this._register(domEvent(pane.dropTargetElement, 'dragend')(this.onDragEnd, this)); - this._register(domEvent(pane.dropTargetElement, 'drop')(this.onDrop, this)); + this._register(addDisposableListener(pane.draggableElement, 'dragstart', e => this.onDragStart(e))); + this._register(addDisposableListener(pane.dropTargetElement, 'dragenter', e => this.onDragEnter(e))); + this._register(addDisposableListener(pane.dropTargetElement, 'dragleave', e => this.onDragLeave(e))); + this._register(addDisposableListener(pane.dropTargetElement, 'dragend', e => this.onDragEnd(e))); + this._register(addDisposableListener(pane.dropTargetElement, 'drop', e => this.onDrop(e))); } private onDragStart(e: DragEvent): void { diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 5996365a70f..2ee283f0a40 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -11,8 +11,7 @@ import { clamp } from 'vs/base/common/numbers'; import { range, pushToStart, pushToEnd } from 'vs/base/common/arrays'; import { Sash, Orientation, ISashEvent as IBaseSashEvent, SashState } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; -import { domEvent } from 'vs/base/browser/event'; -import { $, append, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; @@ -488,8 +487,8 @@ export class SplitView extends Disposable { // This way, we can press Alt while we resize a sash, macOS style! const disposable = combinedDisposable( - domEvent(document.body, 'keydown')(e => resetSashDragState(this.sashDragState!.current, e.altKey)), - domEvent(document.body, 'keyup')(() => resetSashDragState(this.sashDragState!.current, false)) + addDisposableListener(document.body, 'keydown', e => resetSashDragState(this.sashDragState!.current, e.altKey)), + addDisposableListener(document.body, 'keyup', () => resetSashDragState(this.sashDragState!.current, false)) ); const resetSashDragState = (start: number, alt: boolean) => { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index efc69fa4755..c4120542a9f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate, isInputElement, isMonacoEditor } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list'; -import { append, $, getDomNodePagePosition, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom'; +import { append, $, getDomNodePagePosition, hasParentWithClass, createStyleSheet, clearNode, addDisposableListener } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -16,7 +16,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; import { range, equals, distinctES6, firstOrDefault } from 'vs/base/common/arrays'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; -import { domEvent } from 'vs/base/browser/event'; +import { DomEmitter } from 'vs/base/browser/event'; import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; import { localize } from 'vs/nls'; @@ -648,7 +648,7 @@ class TypeFilterController implements IDisposable { ) { this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`); this.domNode.draggable = true; - domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); + this.disposables.add(addDisposableListener(this.domNode, 'dragstart', () => this.onDragStart())); this.messageDomNode = append(view.getHTMLElement(), $(`.monaco-list-type-filter-message`)); @@ -661,7 +661,7 @@ class TypeFilterController implements IDisposable { this.filterOnTypeDomNode.checked = this._filterOnType; this.filterOnTypeDomNode.tabIndex = -1; this.updateFilterOnTypeTitleAndIcon(); - domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); + this.disposables.add(addDisposableListener(this.filterOnTypeDomNode, 'input', () => this.onDidChangeFilterOnType())); this.clearDomNode = append(controls, $('button.clear' + treeFilterClearIcon.cssSelector)); this.clearDomNode.tabIndex = -1; @@ -711,7 +711,8 @@ class TypeFilterController implements IDisposable { return; } - const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) + const onRawKeyDown = this.enabledDisposables.add(new DomEmitter(this.view.getHTMLElement(), 'keydown')); + const onKeyDown = Event.chain(onRawKeyDown.event) .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) .filter(e => e.key !== 'Dead' && !/^Media/.test(e.key)) .map(e => new StandardKeyboardEvent(e)) @@ -721,9 +722,9 @@ class TypeFilterController implements IDisposable { .forEach(e => { e.stopPropagation(); e.preventDefault(); }) .event; - const onClear = domEvent(this.clearDomNode, 'click'); + const onClearClick = this.enabledDisposables.add(new DomEmitter(this.clearDomNode, 'click')); - Event.chain(Event.any(onKeyDown, onClear)) + Event.chain(Event.any(onKeyDown, onClearClick.event)) .event(this.onEventOrInput, this, this.enabledDisposables); this.filter.pattern = ''; @@ -849,8 +850,8 @@ class TypeFilterController implements IDisposable { this.domNode.classList.add('dragging'); disposables.add(toDisposable(() => this.domNode.classList.remove('dragging'))); - domEvent(document, 'dragover')(onDragOver, null, disposables); - domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); + disposables.add(addDisposableListener(document, 'dragover', e => onDragOver(e))); + disposables.add(addDisposableListener(this.domNode, 'dragend', () => onDragEnd())); StaticDND.CurrentDragAndDropData = new DragAndDropData('vscode-ui'); disposables.add(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 2f41f489214..04bf8790174 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { domEvent, stop } from 'vs/base/browser/event'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Event } from 'vs/base/common/event'; @@ -90,11 +89,15 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { const overloads = dom.append(controls, $('.overloads')); const next = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsNextIcon))); - const onPreviousClick = stop(domEvent(previous, 'click')); - this._register(onPreviousClick(this.previous, this)); + this._register(dom.addDisposableListener(previous, 'click', e => { + dom.EventHelper.stop(e); + this.previous(); + })); - const onNextClick = stop(domEvent(next, 'click')); - this._register(onNextClick(this.next, this)); + this._register(dom.addDisposableListener(next, 'click', e => { + dom.EventHelper.stop(e); + this.next(); + })); const body = $('.body'); const scrollbar = new DomScrollableElement(body, {}); diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 581b30f2879..ae5c10aa2d1 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./menuEntryActionViewItem'; -import { asCSSUrl, ModifierKeyEmitter } from 'vs/base/browser/dom'; -import { domEvent } from 'vs/base/browser/event'; +import { addDisposableListener, asCSSUrl, ModifierKeyEmitter } from 'vs/base/browser/dom'; import { IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { IDisposable, toDisposable, MutableDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; @@ -177,12 +176,12 @@ export class MenuEntryActionViewItem extends ActionViewItem { })); } - this._register(domEvent(container, 'mouseleave')(_ => { + this._register(addDisposableListener(container, 'mouseleave', _ => { mouseOver = false; updateAltState(); })); - this._register(domEvent(container, 'mouseenter')(e => { + this._register(addDisposableListener(container, 'mouseenter', _ => { mouseOver = true; updateAltState(); })); diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index bf0652d0571..5597070aff0 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -14,9 +14,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; -import { EventType, $, isHTMLElement } from 'vs/base/browser/dom'; +import { EventType, $, isHTMLElement, addDisposableListener } from 'vs/base/browser/dom'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; -import { domEvent } from 'vs/base/browser/event'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -75,7 +74,9 @@ export class ContextMenuHandler { this.block.style.width = '100%'; this.block.style.height = '100%'; this.block.style.zIndex = '-1'; - domEvent(this.block, EventType.MOUSE_DOWN)((e: MouseEvent) => e.stopPropagation()); + + // TODO@Steven: this is never getting disposed + addDisposableListener(this.block, EventType.MOUSE_DOWN, e => e.stopPropagation()); } const menuDisposables = new DisposableStore(); @@ -94,8 +95,8 @@ export class ContextMenuHandler { menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables); menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables); - domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables); - domEvent(window, EventType.MOUSE_DOWN)((e: MouseEvent) => { + menuDisposables.add(addDisposableListener(window, EventType.BLUR, () => this.contextViewService.hideContextView(true))); + menuDisposables.add(addDisposableListener(window, EventType.MOUSE_DOWN, (e: MouseEvent) => { if (e.defaultPrevented) { return; } @@ -117,7 +118,7 @@ export class ContextMenuHandler { } this.contextViewService.hideContextView(true); - }, null, menuDisposables); + })); return combinedDisposable(menuDisposables, menu); }, diff --git a/src/vs/platform/opener/browser/link.ts b/src/vs/platform/opener/browser/link.ts index 86a95a1cec4..f16859dc459 100644 --- a/src/vs/platform/opener/browser/link.ts +++ b/src/vs/platform/opener/browser/link.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { $, EventHelper, EventLike } from 'vs/base/browser/dom'; -import { DomEmitter, domEvent } from 'vs/base/browser/event'; +import { DomEmitter } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -67,7 +67,8 @@ export class Link extends Disposable { }, link.label); const onClickEmitter = this._register(new DomEmitter(this.el, 'click')); - const onEnterPress = Event.chain(domEvent(this.el, 'keypress')) + const onKeyPress = this._register(new DomEmitter(this.el, 'keypress')); + const onEnterPress = Event.chain(onKeyPress.event) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.Enter) .event; diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 485670a0a69..0e7b3222932 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -22,7 +22,6 @@ import { Color } from 'vs/base/common/color'; import { IBaseActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Codicon } from 'vs/base/common/codicons'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; -import { domEvent } from 'vs/base/browser/event'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; @@ -395,15 +394,15 @@ export class ActivityActionViewItem extends BaseActionViewItem { this.updateTitle(); if (this.useCustomHover) { - this.hoverDisposables.add(domEvent(this.container, EventType.MOUSE_OVER, true)(() => { + this.hoverDisposables.add(addDisposableListener(this.container, EventType.MOUSE_OVER, () => { if (!this.showHoverScheduler.isScheduled()) { this.showHoverScheduler.schedule(this.options.hoverOptions!.delay() || 150); } - })); - this.hoverDisposables.add(domEvent(this.container, EventType.MOUSE_LEAVE, true)(() => { + }, true)); + this.hoverDisposables.add(addDisposableListener(this.container, EventType.MOUSE_LEAVE, () => { this.hover.value = undefined; this.showHoverScheduler.cancel(); - })); + }, true)); this.hoverDisposables.add(toDisposable(() => { this.hover.value = undefined; this.showHoverScheduler.cancel(); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 850045df6e8..b5284a3389d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -37,7 +37,6 @@ import { ITextModel } from 'vs/editor/common/model'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { basename } from 'vs/base/common/path'; -import { domEvent } from 'vs/base/browser/event'; import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -47,6 +46,8 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { Expression } from 'vs/workbench/contrib/debug/common/debugModel'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { addDisposableListener } from 'vs/base/browser/dom'; +import { DomEmitter } from 'vs/base/browser/event'; const LAUNCH_JSON_REGEX = /\.vscode\/launch\.json$/; const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration'; @@ -284,7 +285,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.altListener.dispose(); } // When the alt key is pressed show regular editor hover and hide the debug hover #84561 - this.altListener = domEvent(document, 'keydown')(keydownEvent => { + this.altListener = addDisposableListener(document, 'keydown', keydownEvent => { const standardKeyboardEvent = new StandardKeyboardEvent(keydownEvent); if (standardKeyboardEvent.keyCode === KeyCode.Alt) { this.altPressed = true; @@ -297,7 +298,8 @@ export class DebugEditorContribution implements IDebugEditorContribution { hoverController.showContentHover(this.hoverRange, HoverStartMode.Immediate, false); } - const listener = Event.any(this.hostService.onDidChangeFocus, domEvent(document, 'keyup'))(keyupEvent => { + const onKeyUp = new DomEmitter(document, 'keyup'); + const listener = Event.any(this.hostService.onDidChangeFocus, onKeyUp.event)(keyupEvent => { let standardKeyboardEvent = undefined; if (keyupEvent instanceof KeyboardEvent) { standardKeyboardEvent = new StandardKeyboardEvent(keyupEvent); @@ -306,6 +308,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.altPressed = false; this.editor.updateOptions({ hover: { enabled: false } }); listener.dispose(); + onKeyUp.dispose(); } }); } diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index ba3363c265b..fb02163cf28 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -14,7 +14,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom'; +import { append, $, reset, Dimension, clearNode, addDisposableListener } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -22,7 +22,6 @@ import { EnablementState } from 'vs/workbench/services/extensionManagement/commo import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { memoize } from 'vs/base/common/decorators'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -32,7 +31,6 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { domEvent } from 'vs/base/browser/event'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; @@ -267,8 +265,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { data.root.classList.toggle('odd', index % 2 === 1); - const onError = Event.once(domEvent(data.icon, 'error')); - onError(() => data.icon.src = element.marketplaceInfo?.iconUrlFallback || DefaultIconPath, null, data.elementDisposables); + data.elementDisposables.push(addDisposableListener(data.icon, 'error', () => data.icon.src = element.marketplaceInfo?.iconUrlFallback || DefaultIconPath, { once: true })); data.icon.src = element.marketplaceInfo?.iconUrl || DefaultIconPath; if (!data.icon.complete) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index eb1a614bcc7..2649cb4d954 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -13,7 +13,6 @@ import { Cache, CacheResult } from 'vs/base/common/cache'; import { Action, IAction } from 'vs/base/common/actions'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { domEvent } from 'vs/base/browser/event'; import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType, setParentFlowTo } from 'vs/base/browser/dom'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -349,8 +348,7 @@ export class ExtensionEditor extends EditorPane { this.extensionManifest = new Cache(() => createCancelablePromise(token => extension.getManifest(token))); const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, template.iconContainer, true); - const onError = Event.once(domEvent(template.icon, 'error')); - onError(() => template.icon.src = extension.iconUrlFallback, null, this.transientDisposables); + this.transientDisposables.add(addDisposableListener(template.icon, 'error', () => template.icon.src = extension.iconUrlFallback, { once: true })); template.icon.src = extension.iconUrl; template.name.textContent = extension.displayName; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index d9aadc6c9f5..4cf7ed865ec 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/extension'; -import { append, $ } from 'vs/base/browser/dom'; +import { append, $, addDisposableListener } from 'vs/base/browser/dom'; import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -12,7 +12,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -190,8 +189,7 @@ export class Renderer implements IPagedRenderer { updateEnablement(); this.extensionService.onDidChangeExtensions(() => updateEnablement(), this, data.extensionDisposables); - const onError = Event.once(domEvent(data.icon, 'error')); - onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables); + data.extensionDisposables.push(addDisposableListener(data.icon, 'error', () => data.icon.src = extension.iconUrlFallback, { once: true })); data.icon.src = extension.iconUrl; if (!data.icon.complete) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index 4185fb859ec..08cae35d94e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -9,7 +9,6 @@ import { IDisposable, dispose, Disposable, DisposableStore, toDisposable } from import { Action } from 'vs/base/common/actions'; import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { Event } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IListService, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -167,8 +166,7 @@ export class ExtensionRenderer implements IListRenderer, index: number, data: IExtensionTemplateData): void { const extension = node.element.extension; - const onError = Event.once(domEvent(data.icon, 'error')); - onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables); + data.extensionDisposables.push(dom.addDisposableListener(data.icon, 'error', () => data.icon.src = extension.iconUrlFallback, { once: true })); data.icon.src = extension.iconUrl; if (!data.icon.complete) { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index b7c0896ab08..342387826ba 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -49,7 +49,6 @@ import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { ILabelService } from 'vs/platform/label/common/label'; import { isNumber } from 'vs/base/common/types'; -import { domEvent } from 'vs/base/browser/event'; import { IEditableData } from 'vs/workbench/common/views'; import { IEditorInput } from 'vs/workbench/common/editor'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; @@ -328,13 +327,13 @@ export class FilesRenderer implements ICompressibleTreeRenderer { + disposables.add(DOM.addDisposableListener(templateData.container, 'mousedown', e => { const result = getIconLabelNameFromHTMLElement(e.target); if (result) { compressedNavigationController.setIndex(result.index); } - }, undefined, disposables); + })); disposables.add(toDisposable(() => this.compressedNavigationControllers.delete(stat))); diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 9b97b4d6bb1..4a6565b15f2 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -48,13 +48,13 @@ import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { IFileService } from 'vs/platform/files/common/files'; -import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Progress } from 'vs/platform/progress/common/progress'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { DomEmitter } from 'vs/base/browser/event'; interface IResourceMarkersTemplateData { resourceLabel: IResourceLabel; @@ -417,10 +417,10 @@ class MarkerWidget extends Disposable { this._codeLink.setAttribute('href', codeLink); this._codeLink.tabIndex = 0; - const onClick = Event.chain(domEvent(this._codeLink, 'click')) + const onClick = Event.chain(this._register(new DomEmitter(this._codeLink, 'click')).event) .filter(e => ((this._clickModifierKey === 'meta' && e.metaKey) || (this._clickModifierKey === 'ctrl' && e.ctrlKey) || (this._clickModifierKey === 'alt' && e.altKey))) .event; - const onEnterPress = Event.chain(domEvent(this._codeLink, 'keydown')) + const onEnterPress = Event.chain(this._register(new DomEmitter(this._codeLink, 'keydown')).event) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.Enter) .event; diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index ddab4379a4a..d99d3813322 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -36,7 +36,6 @@ import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionb import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { domEvent } from 'vs/base/browser/event'; import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { withUndefinedAsNull } from 'vs/base/common/types'; @@ -443,7 +442,7 @@ export class MarkersView extends ViewPane implements IMarkersView { })); // move focus to input, whenever a key is pressed in the panel container - this._register(domEvent(parent, 'keydown')(e => { + this._register(dom.addDisposableListener(parent, 'keydown', e => { if (this.keybindingService.mightProducePrintableCharacter(new StandardKeyboardEvent(e))) { this.focusFilter(); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts index 377b19bfdce..962f7faf2a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import * as DOM from 'vs/base/browser/dom'; -import { domEvent } from 'vs/base/browser/event'; import { Delayer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; @@ -49,8 +47,8 @@ export class CellDragAndDropController extends Disposable { this.listInsertionIndicator = DOM.append(insertionIndicatorContainer, $('.cell-list-insertion-indicator')); - this._register(domEvent(document.body, DOM.EventType.DRAG_START, true)(this.onGlobalDragStart.bind(this))); - this._register(domEvent(document.body, DOM.EventType.DRAG_END, true)(this.onGlobalDragEnd.bind(this))); + this._register(DOM.addDisposableListener(document.body, DOM.EventType.DRAG_START, this.onGlobalDragStart.bind(this), true)); + this._register(DOM.addDisposableListener(document.body, DOM.EventType.DRAG_END, this.onGlobalDragEnd.bind(this), true)); const addCellDragListener = (eventType: string, handler: (e: CellDragEvent) => void) => { this._register(DOM.addDisposableListener( @@ -300,7 +298,7 @@ export class CellDragAndDropController extends Disposable { const container = templateData.container; dragHandle.setAttribute('draggable', 'true'); - templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_END)(() => { + templateData.disposables.add(DOM.addDisposableListener(dragHandle, DOM.EventType.DRAG_END, () => { if (!this.notebookEditor.notebookOptions.getLayoutConfiguration().dragAndDropEnabled) { return; } @@ -310,7 +308,7 @@ export class CellDragAndDropController extends Disposable { this.dragCleanup(); })); - templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_START)(event => { + templateData.disposables.add(DOM.addDisposableListener(dragHandle, DOM.EventType.DRAG_START, event => { if (!event.dataTransfer) { return; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 84f8542a453..78f77c53573 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -5,7 +5,6 @@ import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; -import { domEvent } from 'vs/base/browser/event'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -261,7 +260,7 @@ abstract class AbstractCellRenderer { } protected addExpandListener(templateData: BaseCellRenderTemplate): void { - templateData.disposables.add(domEvent(templateData.expandButton, DOM.EventType.CLICK)(() => { + templateData.disposables.add(DOM.addDisposableListener(templateData.expandButton, DOM.EventType.CLICK, () => { if (!templateData.currentRenderedCell) { return; } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 676462d52b6..58982ff59cb 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -34,10 +34,9 @@ import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { deepClone } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension, safeInnerHtml, size } from 'vs/base/browser/dom'; +import { addDisposableListener, Dimension, safeInnerHtml, size } from 'vs/base/browser/dom'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { domEvent } from 'vs/base/browser/event'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; @@ -417,7 +416,7 @@ export class WalkThroughPart extends EditorPane { this.loadTextEditorViewState(input); this.updatedScrollPosition(); this.contentDisposables.push(Gesture.addTarget(innerContent)); - this.contentDisposables.push(domEvent(innerContent, TouchEventType.Change)(e => this.onTouchChange(e as GestureEvent), this, this.disposables)); + this.contentDisposables.push(addDisposableListener(innerContent, TouchEventType.Change, e => this.onTouchChange(e as GestureEvent))); }); } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 7c5c8b61da8..21ab06e1c6a 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -18,7 +18,6 @@ import { IModifierKeyStatus, ModifierKeyEmitter, trackFocus } from 'vs/base/brow import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { domEvent } from 'vs/base/browser/event'; import { memoize } from 'vs/base/common/decorators'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; @@ -30,6 +29,7 @@ import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser import { localize } from 'vs/nls'; import Severity from 'vs/base/common/severity'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { DomEmitter } from 'vs/base/browser/event'; /** * A workspace to open in the workbench can either be: @@ -170,11 +170,12 @@ export class BrowserHostService extends Disposable implements IHostService { @memoize get onDidChangeFocus(): Event { const focusTracker = this._register(trackFocus(window)); + const onVisibilityChange = this._register(new DomEmitter(window.document, 'visibilitychange')); return Event.latch(Event.any( Event.map(focusTracker.onDidFocus, () => this.hasFocus), Event.map(focusTracker.onDidBlur, () => this.hasFocus), - Event.map(domEvent(window.document, 'visibilitychange'), () => this.hasFocus) + Event.map(onVisibilityChange.event, () => this.hasFocus) )); } From 9685c7d2a4305bfed77c26ac001cb32b5bec1abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Jun 2021 16:49:49 +0200 Subject: [PATCH 05/35] clean async tests --- src/vs/base/test/common/async.test.ts | 1120 +++++++++++++------------ 1 file changed, 570 insertions(+), 550 deletions(-) diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 08ff52dfccd..f04be19975a 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -12,235 +12,212 @@ import { Event } from 'vs/base/common/event'; suite('Async', () => { - test('cancelablePromise - set token, don\'t wait for inner promise', function () { - let canceled = 0; - let promise = async.createCancelablePromise(token => { - token.onCancellationRequested(_ => { canceled += 1; }); - return new Promise(resolve => { /*never*/ }); + suite('cancelablePromise', function () { + test('set token, don\'t wait for inner promise', function () { + let canceled = 0; + let promise = async.createCancelablePromise(token => { + token.onCancellationRequested(_ => { canceled += 1; }); + return new Promise(resolve => { /*never*/ }); + }); + let result = promise.then(_ => assert.ok(false), err => { + assert.strictEqual(canceled, 1); + assert.ok(isPromiseCanceledError(err)); + }); + promise.cancel(); + promise.cancel(); // cancel only once + return result; }); - let result = promise.then(_ => assert.ok(false), err => { - assert.strictEqual(canceled, 1); - assert.ok(isPromiseCanceledError(err)); + + test('cancel despite inner promise being resolved', function () { + let canceled = 0; + let promise = async.createCancelablePromise(token => { + token.onCancellationRequested(_ => { canceled += 1; }); + return Promise.resolve(1234); + }); + let result = promise.then(_ => assert.ok(false), err => { + assert.strictEqual(canceled, 1); + assert.ok(isPromiseCanceledError(err)); + }); + promise.cancel(); + return result; + }); + + // Cancelling a sync cancelable promise will fire the cancelled token. + // Also, every `then` callback runs in another execution frame. + test('execution order (sync)', function () { + const order: string[] = []; + + const cancellablePromise = async.createCancelablePromise(token => { + order.push('in callback'); + token.onCancellationRequested(_ => order.push('cancelled')); + return Promise.resolve(1234); + }); + + order.push('afterCreate'); + + const promise = cancellablePromise + .then(undefined, err => null) + .then(() => order.push('finally')); + + cancellablePromise.cancel(); + order.push('afterCancel'); + + return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); + }); + + // Cancelling an async cancelable promise is just the same as a sync cancellable promise. + test('execution order (async)', function () { + const order: string[] = []; + + const cancellablePromise = async.createCancelablePromise(token => { + order.push('in callback'); + token.onCancellationRequested(_ => order.push('cancelled')); + return new Promise(c => setTimeout(c.bind(1234), 0)); + }); + + order.push('afterCreate'); + + const promise = cancellablePromise + .then(undefined, err => null) + .then(() => order.push('finally')); + + cancellablePromise.cancel(); + order.push('afterCancel'); + + return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); + }); + + test('get inner result', async function () { + let promise = async.createCancelablePromise(token => { + return async.timeout(12).then(_ => 1234); + }); + + let result = await promise; + assert.strictEqual(result, 1234); }); - promise.cancel(); - promise.cancel(); // cancel only once - return result; }); - test('cancelablePromise - cancel despite inner promise being resolved', function () { - let canceled = 0; - let promise = async.createCancelablePromise(token => { - token.onCancellationRequested(_ => { canceled += 1; }); - return Promise.resolve(1234); - }); - let result = promise.then(_ => assert.ok(false), err => { - assert.strictEqual(canceled, 1); - assert.ok(isPromiseCanceledError(err)); - }); - promise.cancel(); - return result; - }); + suite('Throttler', function () { + test('non async', function () { + let count = 0; + let factory = () => { + return Promise.resolve(++count); + }; - // Cancelling a sync cancelable promise will fire the cancelled token. - // Also, every `then` callback runs in another execution frame. - test('CancelablePromise execution order (sync)', function () { - const order: string[] = []; + let throttler = new async.Throttler(); - const cancellablePromise = async.createCancelablePromise(token => { - order.push('in callback'); - token.onCancellationRequested(_ => order.push('cancelled')); - return Promise.resolve(1234); - }); - - order.push('afterCreate'); - - const promise = cancellablePromise - .then(undefined, err => null) - .then(() => order.push('finally')); - - cancellablePromise.cancel(); - order.push('afterCancel'); - - return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); - }); - - // Cancelling an async cancelable promise is just the same as a sync cancellable promise. - test('CancelablePromise execution order (async)', function () { - const order: string[] = []; - - const cancellablePromise = async.createCancelablePromise(token => { - order.push('in callback'); - token.onCancellationRequested(_ => order.push('cancelled')); - return new Promise(c => setTimeout(c.bind(1234), 0)); - }); - - order.push('afterCreate'); - - const promise = cancellablePromise - .then(undefined, err => null) - .then(() => order.push('finally')); - - cancellablePromise.cancel(); - order.push('afterCancel'); - - return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); - }); - - test('cancelablePromise - get inner result', async function () { - let promise = async.createCancelablePromise(token => { - return async.timeout(12).then(_ => 1234); - }); - - let result = await promise; - assert.strictEqual(result, 1234); - }); - - test('Throttler - non async', function () { - let count = 0; - let factory = () => { - return Promise.resolve(++count); - }; - - let throttler = new async.Throttler(); - - return Promise.all([ - throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }) - ]).then(() => assert.strictEqual(count, 2)); - }); - - test('Throttler', () => { - let count = 0; - let factory = () => async.timeout(0).then(() => ++count); - - let throttler = new async.Throttler(); - - return Promise.all([ - throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }) - ]).then(() => { return Promise.all([ - throttler.queue(factory).then((result) => { assert.strictEqual(result, 3); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }), - throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }) - ]); + throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }) + ]).then(() => assert.strictEqual(count, 2)); + }); + + test('async', () => { + let count = 0; + let factory = () => async.timeout(0).then(() => ++count); + + let throttler = new async.Throttler(); + + return Promise.all([ + throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }) + ]).then(() => { + return Promise.all([ + throttler.queue(factory).then((result) => { assert.strictEqual(result, 3); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }), + throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }) + ]); + }); + }); + + test('last factory should be the one getting called', function () { + let factoryFactory = (n: number) => () => { + return async.timeout(0).then(() => n); + }; + + let throttler = new async.Throttler(); + + let promises: Promise[] = []; + + promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.strictEqual(n, 1); })); + promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); })); + promises.push(throttler.queue(factoryFactory(3)).then((n) => { assert.strictEqual(n, 3); })); + + return Promise.all(promises); }); }); - test('Throttler - last factory should be the one getting called', function () { - let factoryFactory = (n: number) => () => { - return async.timeout(0).then(() => n); - }; + suite('Delayer', function () { + test('simple', () => { + let count = 0; + let factory = () => { + return Promise.resolve(++count); + }; - let throttler = new async.Throttler(); + let delayer = new async.Delayer(0); + let promises: Promise[] = []; - let promises: Promise[] = []; - - promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.strictEqual(n, 1); })); - promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); })); - promises.push(throttler.queue(factoryFactory(3)).then((n) => { assert.strictEqual(n, 3); })); - - return Promise.all(promises); - }); - - test('Delayer', () => { - let count = 0; - let factory = () => { - return Promise.resolve(++count); - }; - - let delayer = new async.Delayer(0); - let promises: Promise[] = []; - - assert(!delayer.isTriggered()); - - promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); - assert(delayer.isTriggered()); - - promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); - assert(delayer.isTriggered()); - - promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); - assert(delayer.isTriggered()); - - return Promise.all(promises).then(() => { assert(!delayer.isTriggered()); - }); - }); - test('Delayer - simple cancel', function () { - let count = 0; - let factory = () => { - return Promise.resolve(++count); - }; + promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + assert(delayer.isTriggered()); - let delayer = new async.Delayer(0); + promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + assert(delayer.isTriggered()); - assert(!delayer.isTriggered()); + promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + assert(delayer.isTriggered()); - const p = delayer.trigger(factory).then(() => { - assert(false); - }, () => { - assert(true, 'yes, it was cancelled'); + return Promise.all(promises).then(() => { + assert(!delayer.isTriggered()); + }); }); - assert(delayer.isTriggered()); - delayer.cancel(); - assert(!delayer.isTriggered()); + test('simple cancel', function () { + let count = 0; + let factory = () => { + return Promise.resolve(++count); + }; - return p; - }); + let delayer = new async.Delayer(0); - test('Delayer - cancel should cancel all calls to trigger', function () { - let count = 0; - let factory = () => { - return Promise.resolve(++count); - }; - - let delayer = new async.Delayer(0); - let promises: Promise[] = []; - - assert(!delayer.isTriggered()); - - promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); - assert(delayer.isTriggered()); - - promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); - assert(delayer.isTriggered()); - - promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); - assert(delayer.isTriggered()); - - delayer.cancel(); - - return Promise.all(promises).then(() => { assert(!delayer.isTriggered()); - }); - }); - test('Delayer - trigger, cancel, then trigger again', function () { - let count = 0; - let factory = () => { - return Promise.resolve(++count); - }; + const p = delayer.trigger(factory).then(() => { + assert(false); + }, () => { + assert(true, 'yes, it was cancelled'); + }); - let delayer = new async.Delayer(0); - let promises: Promise[] = []; - - assert(!delayer.isTriggered()); - - const p = delayer.trigger(factory).then((result) => { - assert.strictEqual(result, 1); + assert(delayer.isTriggered()); + delayer.cancel(); assert(!delayer.isTriggered()); + return p; + }); + + test('cancel should cancel all calls to trigger', function () { + let count = 0; + let factory = () => { + return Promise.resolve(++count); + }; + + let delayer = new async.Delayer(0); + let promises: Promise[] = []; + + assert(!delayer.isTriggered()); + + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); + assert(delayer.isTriggered()); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); @@ -249,404 +226,445 @@ suite('Async', () => { delayer.cancel(); - const p = Promise.all(promises).then(() => { - promises = []; + return Promise.all(promises).then(() => { + assert(!delayer.isTriggered()); + }); + }); + test('trigger, cancel, then trigger again', function () { + let count = 0; + let factory = () => { + return Promise.resolve(++count); + }; + + let delayer = new async.Delayer(0); + let promises: Promise[] = []; + + assert(!delayer.isTriggered()); + + const p = delayer.trigger(factory).then((result) => { + assert.strictEqual(result, 1); assert(!delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(() => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(() => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); + delayer.cancel(); + const p = Promise.all(promises).then(() => { - assert(!delayer.isTriggered()); - }); + promises = []; - assert(delayer.isTriggered()); + assert(!delayer.isTriggered()); + + promises.push(delayer.trigger(factory).then(() => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + assert(delayer.isTriggered()); + + promises.push(delayer.trigger(factory).then(() => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); })); + assert(delayer.isTriggered()); + + const p = Promise.all(promises).then(() => { + assert(!delayer.isTriggered()); + }); + + assert(delayer.isTriggered()); + + return p; + }); return p; }); + assert(delayer.isTriggered()); + return p; }); - assert(delayer.isTriggered()); + test('last task should be the one getting called', function () { + let factoryFactory = (n: number) => () => { + return Promise.resolve(n); + }; - return p; - }); + let delayer = new async.Delayer(0); + let promises: Promise[] = []; - test('Delayer - last task should be the one getting called', function () { - let factoryFactory = (n: number) => () => { - return Promise.resolve(n); - }; - - let delayer = new async.Delayer(0); - let promises: Promise[] = []; - - assert(!delayer.isTriggered()); - - promises.push(delayer.trigger(factoryFactory(1)).then((n) => { assert.strictEqual(n, 3); })); - promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); })); - promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.strictEqual(n, 3); })); - - const p = Promise.all(promises).then(() => { assert(!delayer.isTriggered()); - }); - assert(delayer.isTriggered()); + promises.push(delayer.trigger(factoryFactory(1)).then((n) => { assert.strictEqual(n, 3); })); + promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); })); + promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.strictEqual(n, 3); })); - return p; - }); + const p = Promise.all(promises).then(() => { + assert(!delayer.isTriggered()); + }); - test('Sequence', () => { - let factoryFactory = (n: number) => () => { - return Promise.resolve(n); - }; + assert(delayer.isTriggered()); - return async.sequence([ - factoryFactory(1), - factoryFactory(2), - factoryFactory(3), - factoryFactory(4), - factoryFactory(5), - ]).then((result) => { - assert.strictEqual(5, result.length); - assert.strictEqual(1, result[0]); - assert.strictEqual(2, result[1]); - assert.strictEqual(3, result[2]); - assert.strictEqual(4, result[3]); - assert.strictEqual(5, result[4]); + return p; }); }); - test('Limiter - sync', function () { - let factoryFactory = (n: number) => () => { - return Promise.resolve(n); - }; + suite('sequence', () => { + test('simple', () => { + let factoryFactory = (n: number) => () => { + return Promise.resolve(n); + }; - let limiter = new async.Limiter(1); - - let promises: Promise[] = []; - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - - return Promise.all(promises).then((res) => { - assert.strictEqual(10, res.length); - - limiter = new async.Limiter(100); - - promises = []; - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - - return Promise.all(promises).then((res) => { - assert.strictEqual(10, res.length); + return async.sequence([ + factoryFactory(1), + factoryFactory(2), + factoryFactory(3), + factoryFactory(4), + factoryFactory(5), + ]).then((result) => { + assert.strictEqual(5, result.length); + assert.strictEqual(1, result[0]); + assert.strictEqual(2, result[1]); + assert.strictEqual(3, result[2]); + assert.strictEqual(4, result[3]); + assert.strictEqual(5, result[4]); }); }); }); - test('Limiter - async', function () { - let factoryFactory = (n: number) => () => async.timeout(0).then(() => n); + suite('Limiter', () => { + test('sync', function () { + let factoryFactory = (n: number) => () => { + return Promise.resolve(n); + }; - let limiter = new async.Limiter(1); - let promises: Promise[] = []; - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); + let limiter = new async.Limiter(1); - return Promise.all(promises).then((res) => { - assert.strictEqual(10, res.length); - - limiter = new async.Limiter(100); - - promises = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { assert.strictEqual(10, res.length); + + limiter = new async.Limiter(100); + + promises = []; + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); + + return Promise.all(promises).then((res) => { + assert.strictEqual(10, res.length); + }); + }); + }); + + test('async', function () { + let factoryFactory = (n: number) => () => async.timeout(0).then(() => n); + + let limiter = new async.Limiter(1); + let promises: Promise[] = []; + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); + + return Promise.all(promises).then((res) => { + assert.strictEqual(10, res.length); + + limiter = new async.Limiter(100); + + promises = []; + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); + + return Promise.all(promises).then((res) => { + assert.strictEqual(10, res.length); + }); + }); + }); + + test('assert degree of paralellism', function () { + let activePromises = 0; + let factoryFactory = (n: number) => () => { + activePromises++; + assert(activePromises < 6); + return async.timeout(0).then(() => { activePromises--; return n; }); + }; + + let limiter = new async.Limiter(5); + + let promises: Promise[] = []; + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); + + return Promise.all(promises).then((res) => { + assert.strictEqual(10, res.length); + assert.deepStrictEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], res); }); }); }); - test('Limiter - assert degree of paralellism', function () { - let activePromises = 0; - let factoryFactory = (n: number) => () => { - activePromises++; - assert(activePromises < 6); - return async.timeout(0).then(() => { activePromises--; return n; }); - }; + suite('Queue', () => { + test('simple', function () { + let queue = new async.Queue(); - let limiter = new async.Limiter(5); + let syncPromise = false; + let f1 = () => Promise.resolve(true).then(() => syncPromise = true); - let promises: Promise[] = []; - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); + let asyncPromise = false; + let f2 = () => async.timeout(10).then(() => asyncPromise = true); - return Promise.all(promises).then((res) => { - assert.strictEqual(10, res.length); - assert.deepStrictEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], res); - }); - }); - - test('Queue - simple', function () { - let queue = new async.Queue(); - - let syncPromise = false; - let f1 = () => Promise.resolve(true).then(() => syncPromise = true); - - let asyncPromise = false; - let f2 = () => async.timeout(10).then(() => asyncPromise = true); - - assert.strictEqual(queue.size, 0); - - queue.queue(f1); - assert.strictEqual(queue.size, 1); - - const p = queue.queue(f2); - assert.strictEqual(queue.size, 2); - return p.then(() => { assert.strictEqual(queue.size, 0); - assert.ok(syncPromise); - assert.ok(asyncPromise); + + queue.queue(f1); + assert.strictEqual(queue.size, 1); + + const p = queue.queue(f2); + assert.strictEqual(queue.size, 2); + return p.then(() => { + assert.strictEqual(queue.size, 0); + assert.ok(syncPromise); + assert.ok(asyncPromise); + }); }); - }); - test('Queue - order is kept', function () { - let queue = new async.Queue(); + test('order is kept', function () { + let queue = new async.Queue(); - let res: number[] = []; + let res: number[] = []; - let f1 = () => Promise.resolve(true).then(() => res.push(1)); - let f2 = () => async.timeout(10).then(() => res.push(2)); - let f3 = () => Promise.resolve(true).then(() => res.push(3)); - let f4 = () => async.timeout(20).then(() => res.push(4)); - let f5 = () => async.timeout(0).then(() => res.push(5)); + let f1 = () => Promise.resolve(true).then(() => res.push(1)); + let f2 = () => async.timeout(10).then(() => res.push(2)); + let f3 = () => Promise.resolve(true).then(() => res.push(3)); + let f4 = () => async.timeout(20).then(() => res.push(4)); + let f5 = () => async.timeout(0).then(() => res.push(5)); - queue.queue(f1); - queue.queue(f2); - queue.queue(f3); - queue.queue(f4); - return queue.queue(f5).then(() => { - assert.strictEqual(res[0], 1); - assert.strictEqual(res[1], 2); - assert.strictEqual(res[2], 3); - assert.strictEqual(res[3], 4); - assert.strictEqual(res[4], 5); + queue.queue(f1); + queue.queue(f2); + queue.queue(f3); + queue.queue(f4); + return queue.queue(f5).then(() => { + assert.strictEqual(res[0], 1); + assert.strictEqual(res[1], 2); + assert.strictEqual(res[2], 3); + assert.strictEqual(res[3], 4); + assert.strictEqual(res[4], 5); + }); }); - }); - test('Queue - errors bubble individually but not cause stop', function () { - let queue = new async.Queue(); + test('errors bubble individually but not cause stop', function () { + let queue = new async.Queue(); - let res: number[] = []; - let error = false; + let res: number[] = []; + let error = false; - let f1 = () => Promise.resolve(true).then(() => res.push(1)); - let f2 = () => async.timeout(10).then(() => res.push(2)); - let f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error'))); - let f4 = () => async.timeout(20).then(() => res.push(4)); - let f5 = () => async.timeout(0).then(() => res.push(5)); + let f1 = () => Promise.resolve(true).then(() => res.push(1)); + let f2 = () => async.timeout(10).then(() => res.push(2)); + let f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error'))); + let f4 = () => async.timeout(20).then(() => res.push(4)); + let f5 = () => async.timeout(0).then(() => res.push(5)); - queue.queue(f1); - queue.queue(f2); - queue.queue(f3).then(undefined, () => error = true); - queue.queue(f4); - return queue.queue(f5).then(() => { - assert.strictEqual(res[0], 1); - assert.strictEqual(res[1], 2); - assert.ok(error); - assert.strictEqual(res[2], 4); - assert.strictEqual(res[3], 5); + queue.queue(f1); + queue.queue(f2); + queue.queue(f3).then(undefined, () => error = true); + queue.queue(f4); + return queue.queue(f5).then(() => { + assert.strictEqual(res[0], 1); + assert.strictEqual(res[1], 2); + assert.ok(error); + assert.strictEqual(res[2], 4); + assert.strictEqual(res[3], 5); + }); }); - }); - test('Queue - order is kept (chained)', function () { - let queue = new async.Queue(); + test('order is kept (chained)', function () { + let queue = new async.Queue(); - let res: number[] = []; + let res: number[] = []; - let f1 = () => Promise.resolve(true).then(() => res.push(1)); - let f2 = () => async.timeout(10).then(() => res.push(2)); - let f3 = () => Promise.resolve(true).then(() => res.push(3)); - let f4 = () => async.timeout(20).then(() => res.push(4)); - let f5 = () => async.timeout(0).then(() => res.push(5)); + let f1 = () => Promise.resolve(true).then(() => res.push(1)); + let f2 = () => async.timeout(10).then(() => res.push(2)); + let f3 = () => Promise.resolve(true).then(() => res.push(3)); + let f4 = () => async.timeout(20).then(() => res.push(4)); + let f5 = () => async.timeout(0).then(() => res.push(5)); - return queue.queue(f1).then(() => { - return queue.queue(f2).then(() => { - return queue.queue(f3).then(() => { - return queue.queue(f4).then(() => { - return queue.queue(f5).then(() => { - assert.strictEqual(res[0], 1); - assert.strictEqual(res[1], 2); - assert.strictEqual(res[2], 3); - assert.strictEqual(res[3], 4); - assert.strictEqual(res[4], 5); + return queue.queue(f1).then(() => { + return queue.queue(f2).then(() => { + return queue.queue(f3).then(() => { + return queue.queue(f4).then(() => { + return queue.queue(f5).then(() => { + assert.strictEqual(res[0], 1); + assert.strictEqual(res[1], 2); + assert.strictEqual(res[2], 3); + assert.strictEqual(res[3], 4); + assert.strictEqual(res[4], 5); + }); }); }); }); }); }); + + test('events', function () { + let queue = new async.Queue(); + + let finished = false; + const onFinished = Event.toPromise(queue.onFinished); + + let res: number[] = []; + + let f1 = () => async.timeout(10).then(() => res.push(2)); + let f2 = () => async.timeout(20).then(() => res.push(4)); + let f3 = () => async.timeout(0).then(() => res.push(5)); + + const q1 = queue.queue(f1); + const q2 = queue.queue(f2); + queue.queue(f3); + + q1.then(() => { + assert.ok(!finished); + q2.then(() => { + assert.ok(!finished); + }); + }); + + return onFinished; + }); }); - test('Queue - events', function () { - let queue = new async.Queue(); + suite('ResourceQueue', () => { + test('simple', function () { + let queue = new async.ResourceQueue(); - let finished = false; - const onFinished = Event.toPromise(queue.onFinished); + const r1Queue = queue.queueFor(URI.file('/some/path')); - let res: number[] = []; + r1Queue.onFinished(() => console.log('DONE')); - let f1 = () => async.timeout(10).then(() => res.push(2)); - let f2 = () => async.timeout(20).then(() => res.push(4)); - let f3 = () => async.timeout(0).then(() => res.push(5)); + const r2Queue = queue.queueFor(URI.file('/some/other/path')); - const q1 = queue.queue(f1); - const q2 = queue.queue(f2); - queue.queue(f3); + assert.ok(r1Queue); + assert.ok(r2Queue); + assert.strictEqual(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned - q1.then(() => { - assert.ok(!finished); - q2.then(() => { - assert.ok(!finished); + let syncPromiseFactory = () => Promise.resolve(undefined); + + r1Queue.queue(syncPromiseFactory); + + return new Promise(c => setTimeout(() => c(), 0)).then(() => { + const r1Queue2 = queue.queueFor(URI.file('/some/path')); + assert.notStrictEqual(r1Queue, r1Queue2); // previous one got disposed after finishing }); }); - - return onFinished; }); - test('ResourceQueue - simple', function () { - let queue = new async.ResourceQueue(); + suite('retry', () => { + test('success case', async () => { + let counter = 0; - const r1Queue = queue.queueFor(URI.file('/some/path')); + const res = await async.retry(() => { + counter++; + if (counter < 2) { + return Promise.reject(new Error('fail')); + } - r1Queue.onFinished(() => console.log('DONE')); + return Promise.resolve(true); + }, 10, 3); - const r2Queue = queue.queueFor(URI.file('/some/other/path')); + assert.strictEqual(res, true); + }); - assert.ok(r1Queue); - assert.ok(r2Queue); - assert.strictEqual(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned - - let syncPromiseFactory = () => Promise.resolve(undefined); - - r1Queue.queue(syncPromiseFactory); - - return new Promise(c => setTimeout(() => c(), 0)).then(() => { - const r1Queue2 = queue.queueFor(URI.file('/some/path')); - assert.notStrictEqual(r1Queue, r1Queue2); // previous one got disposed after finishing + test('error case', async () => { + let expectedError = new Error('fail'); + try { + await async.retry(() => { + return Promise.reject(expectedError); + }, 10, 3); + } catch (error) { + assert.strictEqual(error, error); + } }); }); - test('retry - success case', async () => { - let counter = 0; + suite('TaskSequentializer', () => { + test('pending basics', async function () { + const sequentializer = new async.TaskSequentializer(); - const res = await async.retry(() => { - counter++; - if (counter < 2) { - return Promise.reject(new Error('fail')); - } + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(2323)); + assert.ok(!sequentializer.pending); - return Promise.resolve(true); - }, 10, 3); + // pending removes itself after done + await sequentializer.setPending(1, Promise.resolve()); + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(1)); + assert.ok(!sequentializer.pending); - assert.strictEqual(res, true); - }); + // pending removes itself after done (use async.timeout) + sequentializer.setPending(2, async.timeout(1)); + assert.ok(sequentializer.hasPending()); + assert.ok(sequentializer.hasPending(2)); + assert.strictEqual(sequentializer.hasPending(1), false); + assert.ok(sequentializer.pending); - test('retry - error case', async () => { - let expectedError = new Error('fail'); - try { - await async.retry(() => { - return Promise.reject(expectedError); - }, 10, 3); - } catch (error) { - assert.strictEqual(error, error); - } - }); + await async.timeout(2); + assert.strictEqual(sequentializer.hasPending(), false); + assert.strictEqual(sequentializer.hasPending(2), false); + assert.ok(!sequentializer.pending); + }); - test('TaskSequentializer - pending basics', async function () { - const sequentializer = new async.TaskSequentializer(); + test('pending and next (finishes instantly)', async function () { + const sequentializer = new async.TaskSequentializer(); - assert.ok(!sequentializer.hasPending()); - assert.ok(!sequentializer.hasPending(2323)); - assert.ok(!sequentializer.pending); + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); - // pending removes itself after done - await sequentializer.setPending(1, Promise.resolve()); - assert.ok(!sequentializer.hasPending()); - assert.ok(!sequentializer.hasPending(1)); - assert.ok(!sequentializer.pending); + // next finishes instantly + let nextDone = false; + const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); - // pending removes itself after done (use async.timeout) - sequentializer.setPending(2, async.timeout(1)); - assert.ok(sequentializer.hasPending()); - assert.ok(sequentializer.hasPending(2)); - assert.strictEqual(sequentializer.hasPending(1), false); - assert.ok(sequentializer.pending); + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); - await async.timeout(2); - assert.strictEqual(sequentializer.hasPending(), false); - assert.strictEqual(sequentializer.hasPending(2), false); - assert.ok(!sequentializer.pending); - }); + test('pending and next (finishes after timeout)', async function () { + const sequentializer = new async.TaskSequentializer(); - test('TaskSequentializer - pending and next (finishes instantly)', async function () { - const sequentializer = new async.TaskSequentializer(); + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); - let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + // next finishes after async.timeout + let nextDone = false; + const res = sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; })); - // next finishes instantly - let nextDone = false; - const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); - await res; - assert.ok(pendingDone); - assert.ok(nextDone); - }); + test('pending and multiple next (last one wins)', async function () { + const sequentializer = new async.TaskSequentializer(); - test('TaskSequentializer - pending and next (finishes after timeout)', async function () { - const sequentializer = new async.TaskSequentializer(); + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); - let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + // next finishes after async.timeout + let firstDone = false; + let firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); - // next finishes after async.timeout - let nextDone = false; - const res = sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; })); + let secondDone = false; + let secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); - await res; - assert.ok(pendingDone); - assert.ok(nextDone); - }); + let thirdDone = false; + let thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); - test('TaskSequentializer - pending and multiple next (last one wins)', async function () { - const sequentializer = new async.TaskSequentializer(); + await Promise.all([firstRes, secondRes, thirdRes]); + assert.ok(pendingDone); + assert.ok(!firstDone); + assert.ok(!secondDone); + assert.ok(thirdDone); + }); - let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + test('cancel pending', async function () { + const sequentializer = new async.TaskSequentializer(); - // next finishes after async.timeout - let firstDone = false; - let firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); + let pendingCancelled = false; + sequentializer.setPending(1, async.timeout(1), () => pendingCancelled = true); + sequentializer.cancelPending(); - let secondDone = false; - let secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); - - let thirdDone = false; - let thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); - - await Promise.all([firstRes, secondRes, thirdRes]); - assert.ok(pendingDone); - assert.ok(!firstDone); - assert.ok(!secondDone); - assert.ok(thirdDone); - }); - - test('TaskSequentializer - cancel pending', async function () { - const sequentializer = new async.TaskSequentializer(); - - let pendingCancelled = false; - sequentializer.setPending(1, async.timeout(1), () => pendingCancelled = true); - sequentializer.cancelPending(); - - assert.ok(pendingCancelled); + assert.ok(pendingCancelled); + }); }); test('raceCancellation', async () => { @@ -723,63 +741,65 @@ suite('Async', () => { assert.strictEqual(counter.increment(), 3); }); - test('firstParallel - simple', async () => { - const a = await async.firstParallel([ - Promise.resolve(1), - Promise.resolve(2), - Promise.resolve(3), - ], v => v === 2); - assert.strictEqual(a, 2); - }); - - test('firstParallel - uses null default', async () => { - assert.strictEqual(await async.firstParallel([Promise.resolve(1)], v => v === 2), null); - }); - - test('firstParallel - uses value default', async () => { - assert.strictEqual(await async.firstParallel([Promise.resolve(1)], v => v === 2, 4), 4); - }); - - test('firstParallel - empty', async () => { - assert.strictEqual(await async.firstParallel([], v => v === 2, 4), 4); - }); - - test('firstParallel - cancels', async () => { - let ct1: CancellationToken; - const p1 = async.createCancelablePromise(async (ct) => { - ct1 = ct; - await async.timeout(200, ct); - return 1; - }); - let ct2: CancellationToken; - const p2 = async.createCancelablePromise(async (ct) => { - ct2 = ct; - await async.timeout(2, ct); - return 2; + suite('firstParallel', () => { + test('simple', async () => { + const a = await async.firstParallel([ + Promise.resolve(1), + Promise.resolve(2), + Promise.resolve(3), + ], v => v === 2); + assert.strictEqual(a, 2); }); - assert.strictEqual(await async.firstParallel([p1, p2], v => v === 2, 4), 2); - assert.strictEqual(ct1!.isCancellationRequested, true, 'should cancel a'); - assert.strictEqual(ct2!.isCancellationRequested, true, 'should cancel b'); - }); - - test('firstParallel - rejection handling', async () => { - let ct1: CancellationToken; - const p1 = async.createCancelablePromise(async (ct) => { - ct1 = ct; - await async.timeout(200, ct); - return 1; - }); - let ct2: CancellationToken; - const p2 = async.createCancelablePromise(async (ct) => { - ct2 = ct; - await async.timeout(2, ct); - throw new Error('oh no'); + test('uses null default', async () => { + assert.strictEqual(await async.firstParallel([Promise.resolve(1)], v => v === 2), null); }); - assert.strictEqual(await async.firstParallel([p1, p2], v => v === 2, 4).catch(() => 'ok'), 'ok'); - assert.strictEqual(ct1!.isCancellationRequested, true, 'should cancel a'); - assert.strictEqual(ct2!.isCancellationRequested, true, 'should cancel b'); + test('uses value default', async () => { + assert.strictEqual(await async.firstParallel([Promise.resolve(1)], v => v === 2, 4), 4); + }); + + test('empty', async () => { + assert.strictEqual(await async.firstParallel([], v => v === 2, 4), 4); + }); + + test('cancels', async () => { + let ct1: CancellationToken; + const p1 = async.createCancelablePromise(async (ct) => { + ct1 = ct; + await async.timeout(200, ct); + return 1; + }); + let ct2: CancellationToken; + const p2 = async.createCancelablePromise(async (ct) => { + ct2 = ct; + await async.timeout(2, ct); + return 2; + }); + + assert.strictEqual(await async.firstParallel([p1, p2], v => v === 2, 4), 2); + assert.strictEqual(ct1!.isCancellationRequested, true, 'should cancel a'); + assert.strictEqual(ct2!.isCancellationRequested, true, 'should cancel b'); + }); + + test('rejection handling', async () => { + let ct1: CancellationToken; + const p1 = async.createCancelablePromise(async (ct) => { + ct1 = ct; + await async.timeout(200, ct); + return 1; + }); + let ct2: CancellationToken; + const p2 = async.createCancelablePromise(async (ct) => { + ct2 = ct; + await async.timeout(2, ct); + throw new Error('oh no'); + }); + + assert.strictEqual(await async.firstParallel([p1, p2], v => v === 2, 4).catch(() => 'ok'), 'ok'); + assert.strictEqual(ct1!.isCancellationRequested, true, 'should cancel a'); + assert.strictEqual(ct2!.isCancellationRequested, true, 'should cancel b'); + }); }); suite('DeferredPromise', () => { From bbf64b8a0947646c1629790d39eb90f8c2512360 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 9 Jun 2021 16:51:03 +0200 Subject: [PATCH 06/35] update `glob-parent` to `5.1.2` --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1f35e720d72..e6485be780e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4197,9 +4197,9 @@ glob-parent@^3.0.0, glob-parent@^3.1.0: path-dirname "^1.0.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" From 78c2a0fa4c6c6f2f0d6ef659ffb2f929c7abd56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Jun 2021 16:53:44 +0200 Subject: [PATCH 07/35] fixes #116777 --- src/vs/base/common/async.ts | 2 +- src/vs/base/test/common/async.test.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index fba360da12c..b5a4bbbf029 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -298,7 +298,7 @@ export class Delayer implements IDisposable { } dispose(): void { - this.cancelTimeout(); + this.cancel(); } } diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index f04be19975a..1bbd7b813e0 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -181,6 +181,21 @@ suite('Async', () => { }); }); + suite('ThrottledDelayer', () => { + test('promise should resolve if disposed', async () => { + const throttledDelayer = new async.ThrottledDelayer(100); + const promise = throttledDelayer.trigger(async () => { }, 0); + throttledDelayer.dispose(); + + try { + await promise; + assert.fail('SHOULD NOT BE HERE'); + } catch (err) { + // OK + } + }); + }); + test('simple cancel', function () { let count = 0; let factory = () => { From 4ae99d1027e7f4a10fc54b0baf6217619dbe0550 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 9 Jun 2021 07:58:38 -0700 Subject: [PATCH 08/35] Make off proc service name var more explicit Fixes #123480 --- .../contrib/terminal/browser/terminalService.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index fc6fe60a52a..4d00c56650c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -63,7 +63,7 @@ export class TerminalService implements ITerminalService { private _linkProviderDisposables: Map = new Map(); private _processSupportContextKey: IContextKey; private readonly _localTerminalService?: ILocalTerminalService; - private readonly _offProcessTerminalService?: IOffProcessTerminalService; + private readonly _primaryOffProcessTerminalService?: IOffProcessTerminalService; private _profilesReadyBarrier: AutoOpenBarrier; private _availableProfiles: ITerminalProfile[] | undefined; private _configHelper: TerminalConfigHelper; @@ -210,7 +210,7 @@ export class TerminalService implements ITerminalService { : enableTerminalReconnection ? this._localTerminalsInitPromise = this._reconnectToLocalTerminals() : Promise.resolve(); - this._offProcessTerminalService = !!this._environmentService.remoteAuthority ? this._remoteTerminalService : this._localTerminalService; + this._primaryOffProcessTerminalService = !!this._environmentService.remoteAuthority ? this._remoteTerminalService : this._localTerminalService; initPromise.then(() => this._setConnected()); // Wait up to 5 seconds for profiles to be ready so it's assured that we know the actual @@ -376,12 +376,11 @@ export class TerminalService implements ITerminalService { } private async _detectProfiles(includeDetectedProfiles?: boolean): Promise { - const offProcService = this._offProcessTerminalService; - if (!offProcService) { + if (!this._primaryOffProcessTerminalService) { return this._availableProfiles || []; } const platform = await this._getPlatformKey(); - return offProcService?.getProfiles(this._configurationService.getValue(`${TerminalSettingPrefix.Profiles}${platform}`), this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${platform}`), includeDetectedProfiles); + return this._primaryOffProcessTerminalService?.getProfiles(this._configurationService.getValue(`${TerminalSettingPrefix.Profiles}${platform}`), this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${platform}`), includeDetectedProfiles); } private _onBeforeShutdown(reason: ShutdownReason): boolean | Promise { @@ -441,7 +440,7 @@ export class TerminalService implements ITerminalService { const state: ITerminalsLayoutInfoById = { tabs: this.terminalGroups.map(g => g.getLayoutInfo(g === this.getActiveGroup())) }; - this._offProcessTerminalService?.setTerminalLayoutInfo(state); + this._primaryOffProcessTerminalService?.setTerminalLayoutInfo(state); } @debounce(500) @@ -449,7 +448,7 @@ export class TerminalService implements ITerminalService { if (!this.configHelper.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.title) { return; } - this._offProcessTerminalService?.updateTitle(instance.persistentProcessId, instance.title, instance.titleSource); + this._primaryOffProcessTerminalService?.updateTitle(instance.persistentProcessId, instance.title, instance.titleSource); } @debounce(500) @@ -457,7 +456,7 @@ export class TerminalService implements ITerminalService { if (!this.configHelper.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.icon) { return; } - this._offProcessTerminalService?.updateIcon(instance.persistentProcessId, instance.icon, instance.color); + this._primaryOffProcessTerminalService?.updateIcon(instance.persistentProcessId, instance.icon, instance.color); } private _removeGroup(group: ITerminalGroup): void { From be6cc2c13afa0445b354f300553dd56e9e4e8258 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 9 Jun 2021 17:10:27 +0200 Subject: [PATCH 09/35] accessibility issues also to sana-ajani --- .github/classifier.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/classifier.json b/.github/classifier.json index 1783b82c879..fd27dc85ec6 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -7,7 +7,7 @@ "labels": { "L10N": {"assign": []}, "VIM": {"assign": []}, - "accessibility": { "assign": ["isidorn"]}, + "accessibility": { "assign": ["isidorn", "sana-ajani"]}, "api": {"assign": ["jrieken"]}, "api-finalization": {"assign": []}, "api-proposal": {"assign": ["jrieken"]}, From d48645cf611433994c9861aa92b845400d72d2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E8=A1=A8=E5=93=A5?= Date: Wed, 9 Jun 2021 23:35:43 +0800 Subject: [PATCH 10/35] fix: areLanguageDiagnosticSettingsEqual always return true (#125365) --- .../src/languageFeatures/diagnostics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 0fd8dec5832..4479d3e267d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -137,7 +137,7 @@ class DiagnosticSettings { const currentSettings = this.get(language); const newSettings = f(currentSettings); this._languageSettings.set(language, newSettings); - return areLanguageDiagnosticSettingsEqual(currentSettings, newSettings); + return !areLanguageDiagnosticSettingsEqual(currentSettings, newSettings); } } From e8fc4264e8f7531f372a8618e940c50bf280f45f Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Wed, 9 Jun 2021 08:40:33 -0700 Subject: [PATCH 11/35] Add WASM MIME type (#125782) --- src/vs/platform/webview/common/mimeTypes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/webview/common/mimeTypes.ts b/src/vs/platform/webview/common/mimeTypes.ts index f9a54488d19..75107e297e6 100644 --- a/src/vs/platform/webview/common/mimeTypes.ts +++ b/src/vs/platform/webview/common/mimeTypes.ts @@ -18,6 +18,7 @@ const webviewMimeTypes = new Map([ ['.xhtml', 'application/xhtml+xml'], ['.oft', 'font/otf'], ['.xml', 'application/xml'], + ['.wasm', 'application/wasm'], ]); export function getWebviewContentMimeType(resource: URI): string { From 4a7cf06a1fb76f3a1a294da211504658fe1055c4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 08:07:40 -0700 Subject: [PATCH 12/35] :lipstick: --- .../browser/view/renderers/webviewMessages.ts | 2 +- .../browser/view/renderers/webviewPreloads.ts | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 78fc3ff7654..6a8e0743b11 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -183,7 +183,7 @@ export interface IContentWidgetTopRequest { export interface IViewScrollTopRequestMessage { readonly type: 'view-scroll'; readonly widgets: IContentWidgetTopRequest[]; - readonly markdownPreviews: { id: string; top: number; }[]; + readonly markupCells: { id: string; top: number; }[]; } export interface IScrollRequestMessage { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 2e6964f9de6..cd64b942812 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -683,7 +683,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re } } - for (const cell of event.data.markdownPreviews) { + for (const cell of event.data.markupCells) { const container = document.getElementById(cell.id); if (container) { container.style.top = `${cell.top}px`; @@ -793,7 +793,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re case 'notebookOptions': currentOptions = event.data.options; - // Update markdown previews + // Update markup cells for (const markdownContainer of document.querySelectorAll('.preview')) { setMarkupContainerDraggable(markdownContainer, currentOptions.dragAndDropEnabled); } @@ -1014,11 +1014,11 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re return existing; } - const markdownCell = new MarkupCell(init.cellId, init.mime, init.content, top); - this._markupCells.set(init.cellId, markdownCell); + const cell = new MarkupCell(init.cellId, init.mime, init.content, top); + this._markupCells.set(init.cellId, cell); - await markdownCell.ready; - return markdownCell; + await cell.ready; + return cell; } public async ensureMarkupCells(update: readonly webviewMessages.IMarkupCellInitialization[]): Promise { @@ -1082,7 +1082,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re public readonly ready: Promise; - /// Internal field that holds markdown text + /// Internal field that holds text content private _content: string; constructor(id: string, mime: string, content: string, top: number) { @@ -1161,15 +1161,15 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re setMarkupContainerDraggable(this.element, currentOptions.dragAndDropEnabled); this.element.addEventListener('dragstart', e => { - markdownPreviewDragManager.startDrag(e, this.id); + markupCellDragManager.startDrag(e, this.id); }); this.element.addEventListener('drag', e => { - markdownPreviewDragManager.updateDrag(e, this.id); + markupCellDragManager.updateDrag(e, this.id); }); this.element.addEventListener('dragend', e => { - markdownPreviewDragManager.endDrag(e, this.id); + markupCellDragManager.endDrag(e, this.id); }); } @@ -1254,13 +1254,13 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re }); } - const markdownPreviewDragManager = new class MarkdownPreviewDragManager { + const markupCellDragManager = new class MarkdownPreviewDragManager { private currentDrag: { cellId: string, clientY: number } | undefined; constructor() { document.addEventListener('dragover', e => { - // Allow dropping dragged markdown cells + // Allow dropping dragged markup cells e.preventDefault(); }); From 7e5abebf76d19d7fdd9f07942ee49c798e9b6415 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 08:54:22 -0700 Subject: [PATCH 13/35] Add mimes as a namespace This follows the pattern we have for `Schemes`. I plan on adding a few new mimes here as well to reduce the number of duplicated strings we have floating around the codebase --- src/vs/base/common/mime.ts | 20 ++++++++++--------- src/vs/platform/webview/common/mimeTypes.ts | 4 ++-- src/vs/workbench/browser/dnd.ts | 4 ++-- .../common/editor/binaryEditorModel.ts | 4 ++-- .../debug/common/debugContentProvider.ts | 4 ++-- .../browser/fileBasedRecommendations.ts | 4 ++-- .../view/renderers/backLayerWebView.ts | 2 +- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 368d77953e6..f6dae1adfa5 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -10,9 +10,11 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { DataUri } from 'vs/base/common/resources'; -export const MIME_TEXT = 'text/plain'; -export const MIME_BINARY = 'application/octet-stream'; -export const MIME_UNKNOWN = 'application/unknown'; +export namespace Mimes { + export const text = 'text/plain'; + export const binary = 'application/octet-stream'; + export const unknown = 'application/unknown'; +} export interface ITextMimeAssociation { readonly id: string; @@ -125,7 +127,7 @@ export function guessMimeTypes(resource: URI | null, firstLine?: string): string } if (!path) { - return [MIME_UNKNOWN]; + return [Mimes.unknown]; } path = path.toLowerCase(); @@ -135,24 +137,24 @@ export function guessMimeTypes(resource: URI | null, firstLine?: string): string // 1.) User configured mappings have highest priority const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); if (configuredMime) { - return [configuredMime, MIME_TEXT]; + return [configuredMime, Mimes.text]; } // 2.) Registered mappings have middle priority const registeredMime = guessMimeTypeByPath(path, filename, nonUserRegisteredAssociations); if (registeredMime) { - return [registeredMime, MIME_TEXT]; + return [registeredMime, Mimes.text]; } // 3.) Firstline has lowest priority if (firstLine) { const firstlineMime = guessMimeTypeByFirstline(firstLine); if (firstlineMime) { - return [firstlineMime, MIME_TEXT]; + return [firstlineMime, Mimes.text]; } } - return [MIME_UNKNOWN]; + return [Mimes.unknown]; } function guessMimeTypeByPath(path: string, filename: string, associations: ITextMimeAssociationItem[]): string | null { @@ -240,7 +242,7 @@ export function isUnspecific(mime: string[] | string): boolean { } if (typeof mime === 'string') { - return mime === MIME_BINARY || mime === MIME_TEXT || mime === MIME_UNKNOWN; + return mime === Mimes.binary || mime === Mimes.text || mime === Mimes.unknown; } return mime.length === 1 && isUnspecific(mime[0]); diff --git a/src/vs/platform/webview/common/mimeTypes.ts b/src/vs/platform/webview/common/mimeTypes.ts index 75107e297e6..85b853e4705 100644 --- a/src/vs/platform/webview/common/mimeTypes.ts +++ b/src/vs/platform/webview/common/mimeTypes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime'; +import { getMediaMime, Mimes } from 'vs/base/common/mime'; import { extname } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; @@ -23,5 +23,5 @@ const webviewMimeTypes = new Map([ export function getWebviewContentMimeType(resource: URI): string { const ext = extname(resource.fsPath).toLowerCase(); - return webviewMimeTypes.get(ext) || getMediaMime(resource.fsPath) || MIME_UNKNOWN; + return webviewMimeTypes.get(ext) || getMediaMime(resource.fsPath) || Mimes.unknown; } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index d52307d18f6..f2ad1ba12d5 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -13,7 +13,7 @@ import { FileAccess, Schemas } from 'vs/base/common/network'; import { IBaseTextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; -import { MIME_BINARY } from 'vs/base/common/mime'; +import { Mimes } from 'vs/base/common/mime'; import { isWindows } from 'vs/base/common/platform'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -277,7 +277,7 @@ export function fillEditorsDragData(accessor: ServicesAccessor, resourcesOrEdito // TODO@sandbox this will no longer work when `vscode-file` // is enabled because we block loading resources that are not // inside installation dir - event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(firstFile.resource), FileAccess.asBrowserUri(firstFile.resource).toString()].join(':')); + event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [Mimes.binary, basename(firstFile.resource), FileAccess.asBrowserUri(firstFile.resource).toString()].join(':')); } // Resource URLs: allows to drop multiple file resources to a target in VS Code diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index 7e4e6a7ae46..af36ab952ed 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -6,14 +6,14 @@ import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; -import { MIME_BINARY } from 'vs/base/common/mime'; +import { Mimes } from 'vs/base/common/mime'; /** * An editor model that just represents a resource that can be loaded. */ export class BinaryEditorModel extends EditorModel { - private readonly mime = MIME_BINARY; + private readonly mime = Mimes.binary; private size: number | undefined; private etag: string | undefined; diff --git a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts index d2a0d2298f8..4cacca17688 100644 --- a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts +++ b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts @@ -5,7 +5,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { guessMimeTypes, MIME_TEXT } from 'vs/base/common/mime'; +import { guessMimeTypes, Mimes } from 'vs/base/common/mime'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -94,7 +94,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC } const createErrModel = (errMsg?: string) => { this.debugService.sourceIsNotAvailable(resource); - const languageSelection = this.modeService.create(MIME_TEXT); + const languageSelection = this.modeService.create(Mimes.text); const message = errMsg ? localize('canNotResolveSourceWithError', "Could not load source '{0}': {1}.", resource.path, errMsg) : localize('canNotResolveSource', "Could not load source '{0}'.", resource.path); diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 984defd93ce..3665a7c5fb2 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -20,7 +20,7 @@ import { Schemas } from 'vs/base/common/network'; import { basename, extname } from 'vs/base/common/resources'; import { match } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; -import { MIME_UNKNOWN, guessMimeTypes } from 'vs/base/common/mime'; +import { Mimes, guessMimeTypes } from 'vs/base/common/mime'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -230,7 +230,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } const mimeTypes = guessMimeTypes(uri); - if (mimeTypes.length !== 1 || mimeTypes[0] !== MIME_UNKNOWN) { + if (mimeTypes.length !== 1 || mimeTypes[0] !== Mimes.unknown) { return; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 6d267dcabe4..d3115a4024e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -826,7 +826,7 @@ var requirejs = (function() { this._sendMessageToWebview({ type: 'view-scroll', widgets: widgets, - markdownPreviews, + markupCells: markdownPreviews, }); } From b776d802d999fd769fd884c15e0c6ecac933ee0b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 08:56:43 -0700 Subject: [PATCH 14/35] Add markdown mime --- src/vs/base/common/mime.ts | 1 + .../notebook/browser/notebookEditorWidget.ts | 7 ++-- .../view/output/transforms/richTransform.ts | 3 +- .../contrib/notebook/common/notebookCommon.ts | 7 ++-- .../notebook/test/notebookCommon.test.ts | 35 ++++++++++--------- .../notebook/test/notebookTextModel.test.ts | 15 ++++---- 6 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index f6dae1adfa5..116248ccf12 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -14,6 +14,7 @@ export namespace Mimes { export const text = 'text/plain'; export const binary = 'application/octet-stream'; export const unknown = 'application/unknown'; + export const markdown = 'text/markdown'; } export interface ITextMimeAssociation { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 58f02ff1473..685e57b0530 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -72,6 +72,7 @@ import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOp import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookEditorToolbar } from 'vs/workbench/contrib/notebook/browser/notebookEditorToolbar'; import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; +import { Mimes } from 'vs/base/common/mime'; const $ = DOM.$; @@ -1386,7 +1387,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } await this._webview!.initializeMarkup(requests.map(request => ({ - mime: 'text/markdown', + mime: Mimes.markdown, cellId: request[0].id, cellHandle: request[0].handle, content: request[0].getText(), @@ -1395,7 +1396,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }))); } else { const initRequests = viewModel.viewCells.filter(cell => cell.cellKind === CellKind.Markup).slice(0, 5).map(cell => ({ - cellId: cell.id, cellHandle: cell.handle, content: cell.getText(), offset: -10000, visible: false, mime: 'text/markdown', + cellId: cell.id, cellHandle: cell.handle, content: cell.getText(), offset: -10000, visible: false, mime: Mimes.markdown, })); await this._webview!.initializeMarkup(initRequests); @@ -2264,7 +2265,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const cellTop = this._list.getAbsoluteTopOfElement(cell); await this._webview.showMarkdownPreview({ - mime: 'text/markdown', + mime: Mimes.markdown, cellHandle: cell.handle, cellId: cell.id, content: cell.getText(), diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 5f55e4926fe..51479a7da5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -5,6 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Mimes } from 'vs/base/common/mime'; import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; @@ -270,7 +271,7 @@ class MdRendererContrib extends Disposable implements IOutputRendererContributio } getMimetypes() { - return ['text/markdown']; + return [Mimes.markdown]; } constructor( diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 78cb628688a..7cb83156b65 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -7,6 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult, ISequence } from 'vs/base/common/diff/diff'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; +import { Mimes } from 'vs/base/common/mime'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; @@ -34,14 +35,14 @@ export const NOTEBOOK_DISPLAY_ORDER = [ 'application/javascript', 'text/html', 'image/svg+xml', - 'text/markdown', + Mimes.markdown, 'image/png', 'image/jpeg', 'text/plain' ]; export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER = [ - 'text/markdown', + Mimes.markdown, 'application/json', 'text/plain', 'text/html', @@ -586,7 +587,7 @@ const _mimeTypeInfo = new Map([ ['image/git', { alwaysSecure: true, supportedByCore: true }], ['image/svg+xml', { supportedByCore: true }], ['application/json', { alwaysSecure: true, supportedByCore: true }], - ['text/markdown', { alwaysSecure: true, supportedByCore: true }], + [Mimes.markdown, { alwaysSecure: true, supportedByCore: true }], ['text/plain', { alwaysSecure: true, supportedByCore: true }], ['text/html', { supportedByCore: true }], ['text/x-javascript', { alwaysSecure: true, supportedByCore: true }], // secure because rendered as text, not executed diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index 88da6bd5e79..e1416492a52 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri, NotebookWorkingCopyTypeIdentifier } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { cellRangesToIndexes, cellIndexesToRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; +import { CellKind, CellUri, diff, NotebookWorkingCopyTypeIdentifier, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellIndexesToRanges, cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; +import { setupInstantiationService, TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; suite('NotebookCommon', () => { const instantiationService = setupInstantiationService(); @@ -23,7 +24,7 @@ suite('NotebookCommon', () => { 'application/javascript', 'text/html', 'image/svg+xml', - 'text/markdown', + Mimes.markdown, 'image/png', 'image/jpeg', 'text/plain' @@ -33,7 +34,7 @@ suite('NotebookCommon', () => { 'application/javascript', 'text/html', 'image/svg+xml', - 'text/markdown', + Mimes.markdown, 'image/png', 'image/jpeg', 'text/plain' @@ -43,7 +44,7 @@ suite('NotebookCommon', () => { assert.deepStrictEqual(sortMimeTypes( [ 'application/json', - 'text/markdown', + Mimes.markdown, 'application/javascript', 'text/html', 'text/plain', @@ -56,7 +57,7 @@ suite('NotebookCommon', () => { 'application/javascript', 'text/html', 'image/svg+xml', - 'text/markdown', + Mimes.markdown, 'image/png', 'image/jpeg', 'text/plain' @@ -65,7 +66,7 @@ suite('NotebookCommon', () => { assert.deepStrictEqual(sortMimeTypes( [ - 'text/markdown', + Mimes.markdown, 'application/json', 'text/plain', 'image/jpeg', @@ -79,7 +80,7 @@ suite('NotebookCommon', () => { 'application/javascript', 'text/html', 'image/svg+xml', - 'text/markdown', + Mimes.markdown, 'image/png', 'image/jpeg', 'text/plain' @@ -97,7 +98,7 @@ suite('NotebookCommon', () => { 'application/javascript', 'text/html', 'image/svg+xml', - 'text/markdown', + Mimes.markdown, 'image/png', 'image/jpeg', 'text/plain' @@ -105,14 +106,14 @@ suite('NotebookCommon', () => { [ 'image/png', 'text/plain', - 'text/markdown', + Mimes.markdown, 'text/html', 'application/json' ], defaultDisplayOrder), [ 'image/png', 'text/plain', - 'text/markdown', + Mimes.markdown, 'text/html', 'application/json', 'application/javascript', @@ -123,7 +124,7 @@ suite('NotebookCommon', () => { assert.deepStrictEqual(sortMimeTypes( [ - 'text/markdown', + Mimes.markdown, 'application/json', 'text/plain', 'application/javascript', @@ -136,13 +137,13 @@ suite('NotebookCommon', () => { 'application/json', 'text/html', 'text/html', - 'text/markdown', + Mimes.markdown, 'application/json' ], defaultDisplayOrder), [ 'application/json', 'text/html', - 'text/markdown', + Mimes.markdown, 'application/javascript', 'image/svg+xml', 'image/png', @@ -165,7 +166,7 @@ suite('NotebookCommon', () => { 'text/html' ], [ - 'text/markdown', + Mimes.markdown, 'text/html', 'application/json' ], defaultDisplayOrder), @@ -189,7 +190,7 @@ suite('NotebookCommon', () => { ], [ 'application/vnd-vega*', - 'text/markdown', + Mimes.markdown, 'text/html', 'application/json' ], defaultDisplayOrder), diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 3e799efe395..6fb9f27c3ca 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { CellKind, CellEditType, NotebookTextModelChangedEvent, SelectionStateType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { withTestNotebook, TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; -import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { Mimes } from 'vs/base/common/mime'; import { IModeService } from 'vs/editor/common/services/modeService'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { CellEditType, CellKind, ICellEditOperation, NotebookTextModelChangedEvent, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { setupInstantiationService, TestCell, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; suite('NotebookTextModel', () => { @@ -189,7 +190,7 @@ suite('NotebookTextModel', () => { editType: CellEditType.Output, outputs: [{ outputId: 'someId', - outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('_Hello_') }] + outputs: [{ mime: Mimes.markdown, valueBytes: valueBytesFromString('_Hello_') }] }] }], true, undefined, () => undefined, undefined); @@ -203,7 +204,7 @@ suite('NotebookTextModel', () => { append: true, outputs: [{ outputId: 'someId2', - outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('_Hello2_') }] + outputs: [{ mime: Mimes.markdown, valueBytes: valueBytesFromString('_Hello2_') }] }] }], true, undefined, () => undefined, undefined); @@ -247,7 +248,7 @@ suite('NotebookTextModel', () => { append: true, outputs: [{ outputId: 'append1', - outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('append 1') }] + outputs: [{ mime: Mimes.markdown, valueBytes: valueBytesFromString('append 1') }] }] }, { @@ -256,7 +257,7 @@ suite('NotebookTextModel', () => { append: true, outputs: [{ outputId: 'append2', - outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('append 2') }] + outputs: [{ mime: Mimes.markdown, valueBytes: valueBytesFromString('append 2') }] }] } ], true, undefined, () => undefined, undefined); From c4fa9ff85be86d821de2a496c1a9a3a83ea57e09 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 09:04:20 -0700 Subject: [PATCH 15/35] Use Mimes.text where possible --- src/vs/base/browser/dnd.ts | 5 ++-- .../browser/controller/textAreaInput.ts | 5 ++-- src/vs/editor/common/modes/modesRegistry.ts | 3 +- .../browser/standaloneCodeEditor.ts | 3 +- .../common/userDataSyncStoreService.ts | 5 ++-- src/vs/platform/webview/common/mimeTypes.ts | 2 +- src/vs/workbench/api/common/extHostTypes.ts | 4 +-- .../view/output/transforms/richTransform.ts | 2 +- .../contrib/notebook/common/notebookCommon.ts | 6 ++-- .../notebook/test/notebookCommon.test.ts | 22 +++++++------- .../notebook/test/notebookDiff.test.ts | 29 ++++++++++--------- .../test/notebookEditorKernelManager.test.ts | 3 +- .../notebook/test/notebookEditorModel.test.ts | 3 +- .../test/notebookKernelService.test.ts | 3 +- .../notebook/test/notebookTextModel.test.ts | 10 +++---- .../test/browser/api/extHostTypes.test.ts | 3 +- .../browser/parts/editor/editorModel.test.ts | 3 +- 17 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index c9d07cccfc3..e1a0872271a 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; import { addDisposableListener } from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Mimes } from 'vs/base/common/mime'; /** * A helper that will execute a provided function when the provided HTMLElement receives @@ -70,7 +71,7 @@ export const DataTransfers = { /** * Typically transfer type for copy/paste transfers. */ - TEXT: 'text/plain' + TEXT: Mimes.text }; export function applyDragImage(event: DragEvent, label: string | null, clazz: string): void { diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 37dbdb7f67b..5234f57cd9b 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -11,6 +11,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Mimes } from 'vs/base/common/mime'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ITextAreaWrapper, ITypeData, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState'; @@ -653,7 +654,7 @@ class ClipboardEventUtils { if (e.clipboardData) { e.preventDefault(); - const text = e.clipboardData.getData('text/plain'); + const text = e.clipboardData.getData(Mimes.text); let metadata: ClipboardStoredMetadata | null = null; const rawmetadata = e.clipboardData.getData('vscode-editor-data'); if (typeof rawmetadata === 'string') { @@ -681,7 +682,7 @@ class ClipboardEventUtils { public static setTextData(e: ClipboardEvent, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void { if (e.clipboardData) { - e.clipboardData.setData('text/plain', text); + e.clipboardData.setData(Mimes.text, text); if (typeof html === 'string') { e.clipboardData.setData('text/html', html); } diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index 95877a9bea6..8001a55a159 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -10,6 +10,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { Mimes } from 'vs/base/common/mime'; // Define extension point ids export const Extensions = { @@ -65,7 +66,7 @@ ModesRegistry.registerLanguage({ id: PLAINTEXT_MODE_ID, extensions: [PLAINTEXT_EXTENSION], aliases: [nls.localize('plainText.alias', "Plain Text"), 'text'], - mimetypes: ['text/plain'] + mimetypes: [Mimes.text] }); LanguageConfigurationRegistry.register(PLAINTEXT_LANGUAGE_IDENTIFIER, { brackets: [ diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 77884d5ffde..de8998e0fa0 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -35,6 +35,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ILanguageSelection, IModeService } from 'vs/editor/common/services/modeService'; import { URI } from 'vs/base/common/uri'; import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl'; +import { Mimes } from 'vs/base/common/mime'; /** * Description of an action contribution @@ -425,7 +426,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon let model: ITextModel | null; if (typeof _model === 'undefined') { - model = createTextModel(modelService, modeService, options.value || '', options.language || 'text/plain', undefined); + model = createTextModel(modelService, modeService, options.value || '', options.language || Mimes.text, undefined); this._ownsModel = true; } else { model = _model; diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 803296e4d8c..bc79d037959 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -23,6 +23,7 @@ import { createCancelablePromise, timeout, CancelablePromise } from 'vs/base/com import { isString, isObject, isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; +import { Mimes } from 'vs/base/common/mime'; const SYNC_PREVIOUS_STORE = 'sync.previous.store'; const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until'; @@ -304,7 +305,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); headers = { ...headers }; - headers['Content-Type'] = 'text/plain'; + headers['Content-Type'] = Mimes.text; if (ref) { headers['If-Match'] = ref; } @@ -356,7 +357,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } const url = joinPath(this.userDataSyncStoreUrl, 'resource').toString(); - const headers: IHeaders = { 'Content-Type': 'text/plain' }; + const headers: IHeaders = { 'Content-Type': Mimes.text }; await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None); diff --git a/src/vs/platform/webview/common/mimeTypes.ts b/src/vs/platform/webview/common/mimeTypes.ts index 85b853e4705..d862e9bd65e 100644 --- a/src/vs/platform/webview/common/mimeTypes.ts +++ b/src/vs/platform/webview/common/mimeTypes.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; const webviewMimeTypes = new Map([ ['.svg', 'image/svg+xml'], - ['.txt', 'text/plain'], + ['.txt', Mimes.text], ['.css', 'text/css'], ['.js', 'application/javascript'], ['.json', 'application/json'], diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index ed30c969a70..ecf921e4544 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -8,7 +8,7 @@ import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent'; import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map'; -import { normalizeMimeType } from 'vs/base/common/mime'; +import { Mimes, normalizeMimeType } from 'vs/base/common/mime'; import { isArray, isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -3072,7 +3072,7 @@ export class NotebookCellOutputItem { static #encoder = new TextEncoder(); - static text(value: string, mime: string = 'text/plain'): NotebookCellOutputItem { + static text(value: string, mime: string = Mimes.text): NotebookCellOutputItem { const bytes = NotebookCellOutputItem.#encoder.encode(String(value)); return new NotebookCellOutputItem(bytes, mime); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 51479a7da5c..c4774f45e6f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -216,7 +216,7 @@ class PlainTextRendererContrib extends Disposable implements IOutputRendererCont } getMimetypes() { - return ['text/plain']; + return [Mimes.text]; } constructor( diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 7cb83156b65..669b976936c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -38,13 +38,13 @@ export const NOTEBOOK_DISPLAY_ORDER = [ Mimes.markdown, 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ]; export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER = [ Mimes.markdown, 'application/json', - 'text/plain', + Mimes.text, 'text/html', 'image/svg+xml', 'image/png', @@ -588,7 +588,7 @@ const _mimeTypeInfo = new Map([ ['image/svg+xml', { supportedByCore: true }], ['application/json', { alwaysSecure: true, supportedByCore: true }], [Mimes.markdown, { alwaysSecure: true, supportedByCore: true }], - ['text/plain', { alwaysSecure: true, supportedByCore: true }], + [Mimes.text, { alwaysSecure: true, supportedByCore: true }], ['text/html', { supportedByCore: true }], ['text/x-javascript', { alwaysSecure: true, supportedByCore: true }], // secure because rendered as text, not executed ['application/vnd.code.notebook.error', { alwaysSecure: true, supportedByCore: true }], diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index e1416492a52..82dbeb15c7c 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -27,7 +27,7 @@ suite('NotebookCommon', () => { Mimes.markdown, 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ], [], defaultDisplayOrder), [ 'application/json', @@ -37,7 +37,7 @@ suite('NotebookCommon', () => { Mimes.markdown, 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ] ); @@ -47,7 +47,7 @@ suite('NotebookCommon', () => { Mimes.markdown, 'application/javascript', 'text/html', - 'text/plain', + Mimes.text, 'image/png', 'image/jpeg', 'image/svg+xml' @@ -60,7 +60,7 @@ suite('NotebookCommon', () => { Mimes.markdown, 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ] ); @@ -68,7 +68,7 @@ suite('NotebookCommon', () => { [ Mimes.markdown, 'application/json', - 'text/plain', + Mimes.text, 'image/jpeg', 'application/javascript', 'text/html', @@ -83,7 +83,7 @@ suite('NotebookCommon', () => { Mimes.markdown, 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ] ); }); @@ -101,18 +101,18 @@ suite('NotebookCommon', () => { Mimes.markdown, 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ], [ 'image/png', - 'text/plain', + Mimes.text, Mimes.markdown, 'text/html', 'application/json' ], defaultDisplayOrder), [ 'image/png', - 'text/plain', + Mimes.text, Mimes.markdown, 'text/html', 'application/json', @@ -126,7 +126,7 @@ suite('NotebookCommon', () => { [ Mimes.markdown, 'application/json', - 'text/plain', + Mimes.text, 'application/javascript', 'text/html', 'image/svg+xml', @@ -148,7 +148,7 @@ suite('NotebookCommon', () => { 'image/svg+xml', 'image/png', 'image/jpeg', - 'text/plain' + Mimes.text ] ); }); diff --git a/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts b/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts index 3e612b388c9..324596338b7 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { LcsDiff } from 'vs/base/common/diff/diff'; +import { Mimes } from 'vs/base/common/mime'; import { NotebookDiffEditorEventDispatcher } from 'vs/workbench/contrib/notebook/browser/diff/eventDispatcher'; import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; import { CellKind, CellSequence } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -14,9 +15,9 @@ suite('NotebookCommon', () => { test('diff different source', async () => { await withTestNotebookDiffModel([ - ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], ], [ - ['y', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], + ['y', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], ], (model, accessor) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); const diffResult = diff.ComputeDiff(false); @@ -44,10 +45,10 @@ suite('NotebookCommon', () => { test('diff different output', async () => { await withTestNotebookDiffModel([ - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [5] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [5] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], ['', 'javascript', CellKind.Code, [], {}] ], [ - ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], ['', 'javascript', CellKind.Code, [], {}] ], (model, accessor) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); @@ -145,12 +146,12 @@ suite('NotebookCommon', () => { test('diff foo/foe', async () => { await withTestNotebookDiffModel([ - [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [6] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], - [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [2] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [6] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [2] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], ['', 'javascript', CellKind.Code, [], {}] ], [ - [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [6] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], - [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [2] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [6] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [2] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], ['', 'javascript', CellKind.Code, [], {}] ], (model, accessor) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); @@ -272,13 +273,13 @@ suite('NotebookCommon', () => { await withTestNotebookDiffModel([ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }] ], [ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }] + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }] ], async (model) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); const diffResult = diff.ComputeDiff(false); @@ -305,18 +306,18 @@ suite('NotebookCommon', () => { await withTestNotebookDiffModel([ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }], ['x = 5', 'javascript', CellKind.Code, [], {}], ['x', 'javascript', CellKind.Code, [], {}], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [5] }] }], {}], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [5] }] }], {}], ], [ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], ['x = 5', 'javascript', CellKind.Code, [], {}], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [5] }] }], {}], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, valueBytes: [5] }] }], {}], ['x', 'javascript', CellKind.Code, [], {}], ], async (model) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); diff --git a/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts b/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts index 7e73ddcac71..b08ff6b1e85 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts @@ -20,6 +20,7 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookS import { mock } from 'vs/base/test/common/mock'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Mimes } from 'vs/base/common/mime'; suite('NotebookEditorKernelManager', () => { @@ -151,6 +152,6 @@ class TestNotebookKernel implements INotebookKernel { } constructor(opts?: { languages: string[] }) { - this.supportedLanguages = opts?.languages ?? ['text/plain']; + this.supportedLanguages = opts?.languages ?? [Mimes.text]; } } diff --git a/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts index af1be838a68..e9c802d63c1 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts @@ -25,6 +25,7 @@ import { CellKind, NotebookDataDto, TransientOptions } from 'vs/workbench/contri import { setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Mimes } from 'vs/base/common/mime'; suite('NotebookFileWorkingCopyModel', function () { @@ -35,7 +36,7 @@ suite('NotebookFileWorkingCopyModel', function () { const notebook = instantiationService.createInstance(NotebookTextModel, 'notebook', URI.file('test'), - [{ cellKind: CellKind.Code, language: 'foo', source: 'foo', outputs: [{ outputId: 'id', outputs: [{ mime: 'text/plain', value: 'Hello Out' }] }] }], + [{ cellKind: CellKind.Code, language: 'foo', source: 'foo', outputs: [{ outputId: 'id', outputs: [{ mime: Mimes.text, value: 'Hello Out' }] }] }], {}, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false } ); diff --git a/src/vs/workbench/contrib/notebook/test/notebookKernelService.test.ts b/src/vs/workbench/contrib/notebook/test/notebookKernelService.test.ts index 1d6770c99d7..bd744bb633d 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookKernelService.test.ts @@ -16,6 +16,7 @@ import { mock } from 'vs/base/test/common/mock'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { Mimes } from 'vs/base/common/mime'; suite('NotebookKernelService', () => { @@ -176,7 +177,7 @@ class TestNotebookKernel implements INotebookKernel { } constructor(opts?: { languages?: string[], label?: string, viewType?: string }) { - this.supportedLanguages = opts?.languages ?? ['text/plain']; + this.supportedLanguages = opts?.languages ?? [Mimes.text]; this.label = opts?.label ?? this.label; this.viewType = opts?.viewType ?? this.viewType; } diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 6fb9f27c3ca..2e4413b0c84 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -220,7 +220,7 @@ suite('NotebookTextModel', () => { editType: CellEditType.Output, outputs: [{ outputId: 'someId3', - outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('Last, replaced output') }] + outputs: [{ mime: Mimes.text, valueBytes: valueBytesFromString('Last, replaced output') }] }] }], true, undefined, () => undefined, undefined); @@ -585,7 +585,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, handle: 0, append: true, outputs: [{ outputId: 'newOutput', - outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] + outputs: [{ mime: Mimes.text, valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] } ]; @@ -615,7 +615,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, index: 2, append: true, outputs: [{ outputId: 'newOutput', - outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] + outputs: [{ mime: Mimes.text, valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] } ]; @@ -642,7 +642,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, index: 1, append: true, outputs: [{ outputId: 'newOutput', - outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] + outputs: [{ mime: Mimes.text, valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] }, { @@ -651,7 +651,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, index: 1, append: true, outputs: [{ outputId: 'newOutput2', - outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] + outputs: [{ mime: Mimes.text, valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] } ]; diff --git a/src/vs/workbench/test/browser/api/extHostTypes.test.ts b/src/vs/workbench/test/browser/api/extHostTypes.test.ts index 3d756f8a8be..dd3ab006228 100644 --- a/src/vs/workbench/test/browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypes.test.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import * as types from 'vs/workbench/api/common/extHostTypes'; import { isWindows } from 'vs/base/common/platform'; import { assertType } from 'vs/base/common/types'; +import { Mimes } from 'vs/base/common/mime'; function assertToJSON(a: any, expected: any) { const raw = JSON.stringify(a); @@ -683,7 +684,7 @@ suite('ExtHostTypes', function () { // --- text item = types.NotebookCellOutputItem.text('Hęłlö'); - assert.strictEqual(item.mime, 'text/plain'); + assert.strictEqual(item.mime, Mimes.text); assert.deepStrictEqual(item.data, new TextEncoder().encode('Hęłlö')); item = types.NotebookCellOutputItem.text('Hęłlö', 'foo/bar'); diff --git a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts index 00bade60da7..0a9a487b11f 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts @@ -26,6 +26,7 @@ import { TestTextResourcePropertiesService } from 'vs/workbench/test/common/work import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; +import { Mimes } from 'vs/base/common/mime'; suite('EditorModel', () => { @@ -86,7 +87,7 @@ suite('EditorModel', () => { const model = new MyTextEditorModel(modelService, modeService); await model.resolve(); - model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); + model.createTextEditorModel(createTextBufferFactory('foo'), null!, Mimes.text); assert.strictEqual(model.isResolved(), true); model.dispose(); }); From 3476074d457b3d5282098658c5451d572849e204 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 09:22:18 -0700 Subject: [PATCH 16/35] Extract createMarkupCellInitialization --- .../notebook/browser/notebookEditorWidget.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 685e57b0530..587228ca624 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -73,6 +73,7 @@ import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/vie import { NotebookEditorToolbar } from 'vs/workbench/contrib/notebook/browser/notebookEditorToolbar'; import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { Mimes } from 'vs/base/common/mime'; +import { IMarkupCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages'; const $ = DOM.$; @@ -1386,18 +1387,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - await this._webview!.initializeMarkup(requests.map(request => ({ - mime: Mimes.markdown, - cellId: request[0].id, - cellHandle: request[0].handle, - content: request[0].getText(), - offset: request[1], - visible: false, - }))); + await this._webview!.initializeMarkup(requests.map(([model, offset]) => this.createMarkupCellInitialization(model, offset))); } else { - const initRequests = viewModel.viewCells.filter(cell => cell.cellKind === CellKind.Markup).slice(0, 5).map(cell => ({ - cellId: cell.id, cellHandle: cell.handle, content: cell.getText(), offset: -10000, visible: false, mime: Mimes.markdown, - })); + const initRequests = viewModel.viewCells + .filter(cell => cell.cellKind === CellKind.Markup) + .slice(0, 5) + .map(cell => this.createMarkupCellInitialization(cell, -10000)); + await this._webview!.initializeMarkup(initRequests); // no cached view state so we are rendering the first viewport @@ -1421,6 +1417,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } + private createMarkupCellInitialization(model: ICellViewModel, offset: number): IMarkupCellInitialization { + return ({ + mime: Mimes.markdown, + cellId: model.id, + cellHandle: model.handle, + content: model.getText(), + offset: offset, + visible: false, + }); + } + restoreListViewState(viewState: INotebookEditorViewState | undefined): void { if (viewState?.scrollPosition !== undefined) { this._list.scrollTop = viewState!.scrollPosition.top; From 7446e50a917ea051933d41831782d593b6e61ae8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 09:46:36 -0700 Subject: [PATCH 17/35] Get mime from cell instead of hardcoding markdown --- .../contrib/notebook/browser/notebookBrowser.ts | 1 + .../contrib/notebook/browser/notebookEditorWidget.ts | 5 ++--- .../notebook/browser/viewModel/baseCellViewModel.ts | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 6e00f849c18..c68c0f782b3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -263,6 +263,7 @@ export interface ICellViewModel extends IGenericCellViewModel { handle: number; uri: URI; language: string; + readonly mime: string; cellKind: CellKind; lineNumbers: 'on' | 'off' | 'inherit'; focusMode: CellFocusMode; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 587228ca624..5502c274095 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -72,7 +72,6 @@ import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOp import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookEditorToolbar } from 'vs/workbench/contrib/notebook/browser/notebookEditorToolbar'; import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; -import { Mimes } from 'vs/base/common/mime'; import { IMarkupCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages'; const $ = DOM.$; @@ -1419,7 +1418,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private createMarkupCellInitialization(model: ICellViewModel, offset: number): IMarkupCellInitialization { return ({ - mime: Mimes.markdown, + mime: model.mime, cellId: model.id, cellHandle: model.handle, content: model.getText(), @@ -2272,7 +2271,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const cellTop = this._list.getAbsoluteTopOfElement(cell); await this._webview.showMarkdownPreview({ - mime: Mimes.markdown, + mime: cell.mime, cellHandle: cell.handle, cellId: cell.id, content: cell.getText(), diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index bfb8c5789fc..c6de02de5e0 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -18,6 +18,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; +import { Mimes } from 'vs/base/common/mime'; export abstract class BaseCellViewModel extends Disposable { @@ -46,6 +47,16 @@ export abstract class BaseCellViewModel extends Disposable { return this.model.language; } + get mime(): string { + switch (this.language) { + case 'markdown': + return Mimes.markdown; + + default: + return Mimes.text; + } + } + abstract cellKind: CellKind; private _editState: CellEditState = CellEditState.Preview; From bb01438efce6d7cb91a40543fd5b6911031a481b Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 9 Jun 2021 18:56:56 +0200 Subject: [PATCH 18/35] Log more information to track down reconnection cause --- src/vs/base/parts/ipc/common/ipc.net.ts | 55 +++++++++++++++++-- src/vs/base/parts/ipc/node/ipc.net.ts | 33 ++++++++--- .../remote/browser/browserSocketFactory.ts | 44 +++++++++++++-- .../remote/common/remoteAgentConnection.ts | 26 ++++++++- 4 files changed, 135 insertions(+), 23 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index aad4fb4e92a..5df18696784 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -10,9 +10,54 @@ import { VSBuffer } from 'vs/base/common/buffer'; import * as platform from 'vs/base/common/platform'; import * as process from 'vs/base/common/process'; +export const enum SocketCloseEventType { + NodeSocketCloseEvent = 0, + WebSocketCloseEvent = 1 +} + +export interface NodeSocketCloseEvent { + /** + * The type of the event + */ + readonly type: SocketCloseEventType.NodeSocketCloseEvent; + /** + * `true` if the socket had a transmission error. + */ + readonly hadError: boolean; + /** + * Underlying error. + */ + readonly error: Error | undefined +} + +export interface WebSocketCloseEvent { + /** + * The type of the event + */ + readonly type: SocketCloseEventType.WebSocketCloseEvent; + /** + * Returns the WebSocket connection close code provided by the server. + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + */ + readonly wasClean: boolean; + /** + * Underlying event. + */ + readonly event: any | undefined; +} + +export type SocketCloseEvent = NodeSocketCloseEvent | WebSocketCloseEvent | undefined; + export interface ISocket extends IDisposable { onData(listener: (e: VSBuffer) => void): IDisposable; - onClose(listener: () => void): IDisposable; + onClose(listener: (e: SocketCloseEvent) => void): IDisposable; onEnd(listener: () => void): IDisposable; write(buffer: VSBuffer): void; end(): void; @@ -624,8 +669,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { private readonly _onDidDispose = new BufferedEmitter(); readonly onDidDispose: Event = this._onDidDispose.event; - private readonly _onSocketClose = new BufferedEmitter(); - readonly onSocketClose: Event = this._onSocketClose.event; + private readonly _onSocketClose = new BufferedEmitter(); + readonly onSocketClose: Event = this._onSocketClose.event; private readonly _onSocketTimeout = new BufferedEmitter(); readonly onSocketTimeout: Event = this._onSocketTimeout.event; @@ -658,7 +703,7 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._socketReader = new ProtocolReader(this._socket); this._socketDisposables.push(this._socketReader); this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg))); - this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire())); + this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e))); if (initialChunk) { this._socketReader.acceptChunk(initialChunk); } @@ -768,7 +813,7 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._socketReader = new ProtocolReader(this._socket); this._socketDisposables.push(this._socketReader); this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg))); - this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire())); + this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e))); this._socketReader.acceptChunk(initialDataChunk); } diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index aa2512c4272..7ce02bb986b 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -13,7 +13,7 @@ import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net'; +import { ISocket, Protocol, Client, ChunkStream, SocketCloseEvent, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Platform, platform } from 'vs/base/common/platform'; @@ -54,10 +54,17 @@ export class NodeSocket implements ISocket { }; } - public onClose(listener: () => void): IDisposable { - this.socket.on('close', listener); + public onClose(listener: (e: SocketCloseEvent) => void): IDisposable { + const adapter = (hadError: boolean) => { + listener({ + type: SocketCloseEventType.NodeSocketCloseEvent, + hadError: hadError, + error: undefined + }); + }; + this.socket.on('close', adapter); return { - dispose: () => this.socket.off('close', listener) + dispose: () => this.socket.off('close', adapter) }; } @@ -167,7 +174,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { private readonly _pendingDeflateData: Buffer[] = []; private readonly _incomingData: ChunkStream; private readonly _onData = this._register(new Emitter()); - private readonly _onClose = this._register(new Emitter()); + private readonly _onClose = this._register(new Emitter()); private _isEnded: boolean = false; private readonly _state = { @@ -232,7 +239,11 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { // zlib errors are fatal, since we have no idea how to recover console.error(err); onUnexpectedError(err); - this._onClose.fire(); + this._onClose.fire({ + type: SocketCloseEventType.NodeSocketCloseEvent, + hadError: true, + error: err + }); }); this._zlibInflate.on('data', (data: Buffer) => { this._pendingInflateData.push(data); @@ -251,7 +262,11 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { // zlib errors are fatal, since we have no idea how to recover console.error(err); onUnexpectedError(err); - this._onClose.fire(); + this._onClose.fire({ + type: SocketCloseEventType.NodeSocketCloseEvent, + hadError: true, + error: err + }); }); this._zlibDeflate.on('data', (data: Buffer) => { this._pendingDeflateData.push(data); @@ -263,7 +278,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { this._zlibDeflateFlushWaitingCount = 0; this._incomingData = new ChunkStream(); this._register(this.socket.onData(data => this._acceptChunk(data))); - this._register(this.socket.onClose(() => this._onClose.fire())); + this._register(this.socket.onClose((e) => this._onClose.fire(e))); } public override dispose(): void { @@ -282,7 +297,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { return this._onData.event(listener); } - public onClose(listener: () => void): IDisposable { + public onClose(listener: (e: SocketCloseEvent) => void): IDisposable { return this._onClose.event(listener); } diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts index 2f343e841ab..abf7428a8ff 100644 --- a/src/vs/platform/remote/browser/browserSocketFactory.ts +++ b/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ISocketFactory, IConnectCallback } from 'vs/platform/remote/common/remoteAgentConnection'; -import { ISocket } from 'vs/base/parts/ipc/common/ipc.net'; +import { ISocket, SocketCloseEvent, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net'; import { VSBuffer } from 'vs/base/common/buffer'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; @@ -16,10 +16,29 @@ export interface IWebSocketFactory { create(url: string): IWebSocket; } +export interface IWebSocketCloseEvent { + /** + * Returns the WebSocket connection close code provided by the server. + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + */ + readonly wasClean: boolean; + /** + * Underlying event. + */ + readonly event: any | undefined; +} + export interface IWebSocket { readonly onData: Event; readonly onOpen: Event; - readonly onClose: Event; + readonly onClose: Event; readonly onError: Event; send(data: ArrayBuffer | ArrayBufferView): void; @@ -33,7 +52,7 @@ class BrowserWebSocket extends Disposable implements IWebSocket { public readonly onOpen: Event; - private readonly _onClose = this._register(new Emitter()); + private readonly _onClose = this._register(new Emitter()); public readonly onClose = this._onClose.event; private readonly _onError = this._register(new Emitter()); @@ -135,7 +154,7 @@ class BrowserWebSocket extends Disposable implements IWebSocket { } } - this._onClose.fire(); + this._onClose.fire({ code: e.code, reason: e.reason, wasClean: e.wasClean, event: e }); })); this._register(dom.addDisposableListener(this._socket, 'error', sendErrorSoon)); @@ -178,8 +197,21 @@ class BrowserSocket implements ISocket { return this.socket.onData((data) => listener(VSBuffer.wrap(new Uint8Array(data)))); } - public onClose(listener: () => void): IDisposable { - return this.socket.onClose(listener); + public onClose(listener: (e: SocketCloseEvent) => void): IDisposable { + const adapter = (e: IWebSocketCloseEvent | void) => { + if (typeof e === 'undefined') { + listener(e); + } else { + listener({ + type: SocketCloseEventType.WebSocketCloseEvent, + code: e.code, + reason: e.reason, + wasClean: e.wasClean, + event: e.event + }); + } + }; + return this.socket.onClose(adapter); } public onEnd(listener: () => void): IDisposable { diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index 41475982dd3..cf5ce3c714b 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Client, PersistentProtocol, ISocket, ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net'; +import { Client, PersistentProtocol, ISocket, ProtocolConstants, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net'; import { generateUuid } from 'vs/base/common/uuid'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; @@ -532,8 +532,28 @@ abstract class PersistentConnection extends Disposable { this._onDidStateChange.fire(new ConnectionGainEvent(this.reconnectionToken, 0, 0)); - this._register(protocol.onSocketClose(() => this._beginReconnecting())); - this._register(protocol.onSocketTimeout(() => this._beginReconnecting())); + this._register(protocol.onSocketClose((e) => { + const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true); + if (!e) { + this._options.logService.info(`${logPrefix} received socket close event.`); + } else if (e.type === SocketCloseEventType.NodeSocketCloseEvent) { + this._options.logService.info(`${logPrefix} received socket close event (hadError: ${e.hadError}).`); + if (e.error) { + this._options.logService.error(e.error); + } + } else { + this._options.logService.info(`${logPrefix} received socket close event (wasClean: ${e.wasClean}, code: ${e.code}, reason: ${e.reason}).`); + if (e.event) { + this._options.logService.error(e.event); + } + } + this._beginReconnecting(); + })); + this._register(protocol.onSocketTimeout(() => { + const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true); + this._options.logService.trace(`${logPrefix} received socket timeout event.`); + this._beginReconnecting(); + })); PersistentConnection._instances.push(this); From 6f6147f335b14bbe423f7ab273ea6779f926de4a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 10:53:13 -0700 Subject: [PATCH 19/35] Events should be readonly --- src/vs/vscode.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 2939fa91371..591f624d9e5 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -9902,7 +9902,7 @@ declare module 'vscode' { /** * An event signaling when the active items have changed. */ - readonly onDidChangeActive: Event; + readonly onDidChangeActive: Event; /** * Selected items. This can be read and updated by the extension. @@ -9912,7 +9912,7 @@ declare module 'vscode' { /** * An event signaling when the selected items have changed. */ - readonly onDidChangeSelection: Event; + readonly onDidChangeSelection: Event; } /** From 72c5213004e3cd5b162158fc1d38f943f9509681 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 10:56:15 -0700 Subject: [PATCH 20/35] Comment spelling --- src/vs/vscode.d.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 591f624d9e5..dca0e8a61fa 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -11427,7 +11427,7 @@ declare module 'vscode' { readonly outputs: readonly NotebookCellOutput[]; /** - * The most recent {@link NotebookCellExecutionSummary excution summary} for this cell. + * The most recent {@link NotebookCellExecutionSummary execution summary} for this cell. */ readonly executionSummary?: NotebookCellExecutionSummary; } @@ -11495,7 +11495,7 @@ declare module 'vscode' { /** * Get the cells of this notebook. A subset can be retrieved by providing - * a range. The range will be adjuset to the notebook. + * a range. The range will be adjusted to the notebook. * * @param range A notebook range. * @returns The cells contained by the range or all cells. @@ -11533,7 +11533,7 @@ declare module 'vscode' { } /** - * A notebook range represents an ordered pair of two cell indicies. + * A notebook range represents an ordered pair of two cell indices. * It is guaranteed that start is less than or equal to end. */ export class NotebookRange { @@ -11644,7 +11644,7 @@ declare module 'vscode' { data: Uint8Array; /** - * Create a new notbook cell output item. + * Create a new notebook cell output item. * * @param data The value of the output item. * @param mime The mime type of the output item. @@ -12140,7 +12140,7 @@ declare module 'vscode' { /** * Namespace for notebooks. * - * The notebooks functionality is composed of three loosly coupled components: + * The notebooks functionality is composed of three loosely coupled components: * * 1. {@link NotebookSerializer} enable the editor to open, show, and save notebooks * 2. {@link NotebookController} own the execution of notebooks, e.g they create output from code cells. @@ -13530,17 +13530,17 @@ declare module 'vscode' { */ export interface AuthenticationProviderAuthenticationSessionsChangeEvent { /** - * The {@link AuthenticationSession}s of the {@link AuthentiationProvider AuthenticationProvider} that have been added. + * The {@link AuthenticationSession}s of the {@link AuthenticationProvider} that have been added. */ readonly added?: readonly AuthenticationSession[]; /** - * The {@link AuthenticationSession}s of the {@link AuthentiationProvider AuthenticationProvider} that have been removed. + * The {@link AuthenticationSession}s of the {@link AuthenticationProvider} that have been removed. */ readonly removed?: readonly AuthenticationSession[]; /** - * The {@link AuthenticationSession}s of the {@link AuthentiationProvider AuthenticationProvider} that have been changed. + * The {@link AuthenticationSession}s of the {@link AuthenticationProvider} that have been changed. * A session changes when its data excluding the id are updated. An example of this is a session refresh that results in a new * access token being set for the session. */ From 8802cd78492f7f68cee64f1a0109e03e57127ed8 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 9 Jun 2021 10:59:34 -0700 Subject: [PATCH 21/35] Fix #125869 --- .../contrib/welcome/gettingStarted/browser/gettingStarted.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css index d6a257a8632..e4777d7c997 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -524,6 +524,7 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer button:focus { outline-style: solid; + outline-width: 1px; } .monaco-workbench .part.editor > .content .gettingStartedContainer .prev-button.button-link { From ae5a300389bd81a49337416c05e8374c8c8a07cd Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 9 Jun 2021 11:06:52 -0700 Subject: [PATCH 22/35] add workspace trust docs label --- .github/commands.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/commands.json b/.github/commands.json index 388a9c3dbb3..030d48c62bb 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -409,5 +409,11 @@ "gjsjohnmurray", "IllusionMH" ] + }, + { + "type": "label", + "name": "*workspace-trust-docs", + "action": "close", + "comment": "This issue appears to be the result of the new workspace trust feature shipped in June 2021. This security-focused feature has major impact on the functionality of VS Code. Due to the volume of issues, we ask that you take some time to review our [comprehensive documentation](https://aka.ms/vscode-workspace-trust) on the feature. If your issue is still not resolved, please let us know." } ] From c6f3512eddc6afa3cae5b4b0391de295c5f147f0 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 9 Jun 2021 11:14:57 -0700 Subject: [PATCH 23/35] Fix #124345 --- .../contrib/welcome/gettingStarted/browser/gettingStarted.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css index e4777d7c997..ada1f483fcc 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -515,6 +515,7 @@ color: inherit; text-align: left; padding: 16px; + font-size: 13px; margin: 1px 0; /* makes room for focus border */ font-family: inherit; } From 33f84b36fd6757d37d3c8f5e2da0653b371641ec Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Jun 2021 20:36:42 +0200 Subject: [PATCH 24/35] tests - disable getting started in integration tests and smoke tests (#125808) * tests - disable getting started in integration tests and smoke tests * --skip-getting-started => --skip-welcome Co-authored-by: Jackson Kearl --- scripts/test-integration.bat | 2 +- scripts/test-integration.sh | 2 +- src/vs/platform/environment/common/argv.ts | 1 + src/vs/platform/environment/node/argv.ts | 1 + .../contrib/welcome/page/browser/welcomePage.ts | 10 ++++++++-- .../services/environment/browser/environmentService.ts | 1 + .../services/environment/common/environmentService.ts | 1 + .../environment/electron-sandbox/environmentService.ts | 3 +++ test/automation/src/code.ts | 1 + 9 files changed, 18 insertions(+), 4 deletions(-) diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index d4cda8cde87..c9055f4051a 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -46,7 +46,7 @@ if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in the extension host -set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-sandbox --no-cached-data --disable-updates --disable-keytar --disable-extensions --disable-workspace-trust --user-data-dir=%VSCODEUSERDATADIR% +set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --skip-welcome --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-keytar --disable-extensions --disable-workspace-trust --user-data-dir=%VSCODEUSERDATADIR% call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index e147156da33..f88bcb2d0d3 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -62,7 +62,7 @@ after_suite # Tests in the extension host -ALL_PLATFORMS_API_TESTS_EXTRA_ARGS="--disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-keytar --disable-extensions --disable-workspace-trust --user-data-dir=$VSCODEUSERDATADIR" +ALL_PLATFORMS_API_TESTS_EXTRA_ARGS="--disable-telemetry --skip-welcome --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-keytar --disable-extensions --disable-workspace-trust --user-data-dir=$VSCODEUSERDATADIR" "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index ded07ffcd9e..d13c087349f 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -60,6 +60,7 @@ export interface NativeParsedArgs { 'enable-proposed-api'?: string[]; // undefined or array of 1 or more 'open-url'?: boolean; 'skip-release-notes'?: boolean; + 'skip-welcome'?: boolean; 'disable-telemetry'?: boolean; 'export-default-configuration'?: string; 'install-source'?: string; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 238098af3a0..a312a4209c3 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -94,6 +94,7 @@ export const OPTIONS: OptionDescriptions> = { 'driver': { type: 'string' }, 'logExtensionHostCommunication': { type: 'boolean' }, 'skip-release-notes': { type: 'boolean' }, + 'skip-welcome': { type: 'boolean' }, 'disable-telemetry': { type: 'boolean' }, 'disable-updates': { type: 'boolean' }, 'disable-keytar': { type: 'boolean' }, diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 0824a9edbbb..150af22a056 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -49,6 +49,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { GettingStartedInput, gettingStartedInputTypeId } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedInput'; import { welcomeButtonBackground, welcomeButtonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; const configurationKey = 'workbench.startupEditor'; @@ -68,6 +69,7 @@ export class WelcomePageContribution implements IWorkbenchContribution { @ILifecycleService private readonly lifecycleService: ILifecycleService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @ICommandService private readonly commandService: ICommandService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { // Run immediately to minimize time spent waiting for exp service. @@ -84,7 +86,7 @@ export class WelcomePageContribution implements IWorkbenchContribution { } private async run() { - const enabled = isWelcomePageEnabled(this.configurationService, this.contextService); + const enabled = isWelcomePageEnabled(this.configurationService, this.contextService, this.environmentService); if (enabled && this.lifecycleService.startupKind !== StartupKind.ReloadedWindow) { const hasBackups = await this.workingCopyBackupService.hasBackups(); if (hasBackups) { return; } @@ -152,7 +154,11 @@ export class WelcomePageContribution implements IWorkbenchContribution { } } -function isWelcomePageEnabled(configurationService: IConfigurationService, contextService: IWorkspaceContextService) { +function isWelcomePageEnabled(configurationService: IConfigurationService, contextService: IWorkspaceContextService, environmentService: IWorkbenchEnvironmentService) { + if (environmentService.skipWelcome) { + return false; + } + const startupEditor = configurationService.inspect(configurationKey); if (!startupEditor.userValue && !startupEditor.workspaceValue) { const welcomeEnabled = configurationService.inspect(oldConfigurationKey); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 00b781462ea..c9aca19246d 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -245,6 +245,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment get logExtensionHostCommunication(): boolean { return this.payload?.get('logExtensionHostCommunication') === 'true'; } get skipReleaseNotes(): boolean { return false; } + get skipWelcome(): boolean { return false; } @memoize get disableWorkspaceTrust(): boolean { return true; } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index f8950d42da8..61b26abff10 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -38,6 +38,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly webviewExternalEndpoint: string; readonly skipReleaseNotes: boolean; + readonly skipWelcome: boolean; readonly debugRenderer: boolean; diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index 948a3aa8935..6ebcaf2855a 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -71,6 +71,9 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment @memoize get skipReleaseNotes(): boolean { return !!this.args['skip-release-notes']; } + @memoize + get skipWelcome(): boolean { return !!this.args['skip-welcome']; } + @memoize get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; } diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 8333471680a..ed64781f1cf 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -135,6 +135,7 @@ export async function spawn(options: SpawnOptions): Promise { const args = [ options.workspacePath, '--skip-release-notes', + '--skip-welcome', '--disable-telemetry', '--no-cached-data', '--disable-updates', From 8ea6989af4c5cddd242078bfab69171452b38615 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 9 Jun 2021 12:37:57 -0700 Subject: [PATCH 25/35] testing: scope context more strictly Fixes https://github.com/microsoft/vscode/issues/125311 --- .../testing/browser/testExplorerActions.ts | 110 ++++++++++++------ .../testing/common/testResultService.ts | 4 + .../testing/common/testingContextKeys.ts | 1 + 3 files changed, 80 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 58e4db282f8..6e86032e748 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -13,7 +13,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; import { Action2, IAction2Options, MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ContextKeyAndExpr, ContextKeyEqualsExpr, ContextKeyFalseExpr, ContextKeyTrueExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyAndExpr, ContextKeyEqualsExpr, ContextKeyFalseExpr, ContextKeyGreaterExpr, ContextKeyTrueExpr } from 'vs/platform/contextkey/common/contextkey'; import { IFileService } from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -59,13 +59,14 @@ const enum ActionOrder { Refresh, } +const hasAnyTestProvider = ContextKeyGreaterExpr.create(TestingContextKeys.providerCount.key, 0); + export class HideTestAction extends Action2 { public static readonly ID = 'testing.hideTest'; constructor() { super({ id: HideTestAction.ID, title: localize('hideTest', 'Hide Test'), - f1: false, menu: { id: MenuId.TestItem, when: TestingContextKeys.testItemIsHidden.isEqualTo(false) @@ -90,7 +91,6 @@ export class UnhideTestAction extends Action2 { super({ id: UnhideTestAction.ID, title: localize('unhideTest', 'Unhide Test'), - f1: false, menu: { id: MenuId.TestItem, when: TestingContextKeys.testItemIsHidden.isEqualTo(true) @@ -116,7 +116,6 @@ export class DebugAction extends Action2 { id: DebugAction.ID, title: localize('debug test', 'Debug Test'), icon: icons.testingDebugIcon, - f1: false, menu: { id: MenuId.TestItem, group: 'inline', @@ -142,7 +141,6 @@ export class RunAction extends Action2 { id: RunAction.ID, title: localize('run test', 'Run Test'), icon: icons.testingRunIcon, - f1: false, menu: { id: MenuId.TestItem, group: 'inline', @@ -268,10 +266,9 @@ abstract class RunOrDebugAllAllAction extends Action2 { id, title, icon, - f1: true, category, keybinding, - menu: { + menu: [{ id: MenuId.ViewTitle, order: debug ? ActionOrder.Debug : ActionOrder.Run, group: 'navigation', @@ -282,7 +279,10 @@ abstract class RunOrDebugAllAllAction extends Action2 { ? TestingContextKeys.hasDebuggableTests.isEqualTo(true) : TestingContextKeys.hasRunnableTests.isEqualTo(true), ]) - } + }, { + id: MenuId.CommandPalette, + when: hasAnyTestProvider, + }] }); } @@ -394,7 +394,6 @@ export class TestingViewAsListAction extends ViewAction { id: TestingViewAsListAction.ID, viewId: Testing.ExplorerViewId, title: localize('testing.viewAsList', "View as List"), - f1: false, toggled: TestingContextKeys.viewMode.isEqualTo(TestExplorerViewMode.List), menu: { id: MenuId.ViewTitle, @@ -420,7 +419,6 @@ export class TestingViewAsTreeAction extends ViewAction { id: TestingViewAsTreeAction.ID, viewId: Testing.ExplorerViewId, title: localize('testing.viewAsTree', "View as Tree"), - f1: false, toggled: TestingContextKeys.viewMode.isEqualTo(TestExplorerViewMode.Tree), menu: { id: MenuId.ViewTitle, @@ -447,7 +445,6 @@ export class TestingSortByNameAction extends ViewAction { id: TestingSortByNameAction.ID, viewId: Testing.ExplorerViewId, title: localize('testing.sortByName', "Sort by Name"), - f1: false, toggled: TestingContextKeys.viewSorting.isEqualTo(TestExplorerViewSorting.ByName), menu: { id: MenuId.ViewTitle, @@ -473,7 +470,6 @@ export class TestingSortByLocationAction extends ViewAction id: TestingSortByLocationAction.ID, viewId: Testing.ExplorerViewId, title: localize('testing.sortByLocation', "Sort by Location"), - f1: false, toggled: TestingContextKeys.viewSorting.isEqualTo(TestExplorerViewSorting.ByLocation), menu: { id: MenuId.ViewTitle, @@ -498,19 +494,22 @@ export class ShowMostRecentOutputAction extends Action2 { super({ id: ShowMostRecentOutputAction.ID, title: localize('testing.showMostRecentOutput', "Show Output"), - f1: true, category, icon: Codicon.terminal, keybinding: { weight: KeybindingWeight.WorkbenchContrib, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.US_SEMICOLON, KeyMod.CtrlCmd | KeyCode.KEY_O), }, - menu: { + precondition: TestingContextKeys.hasAnyResults.isEqualTo(true), + menu: [{ id: MenuId.ViewTitle, order: ActionOrder.Collapse, group: 'navigation', - when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) - } + when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId), + }, { + id: MenuId.CommandPalette, + when: TestingContextKeys.hasAnyResults.isEqualTo(true) + }] }); } @@ -527,7 +526,6 @@ export class CollapseAllAction extends ViewAction { id: CollapseAllAction.ID, viewId: Testing.ExplorerViewId, title: localize('testing.collapseAll', "Collapse All Tests"), - f1: false, icon: Codicon.collapseAll, menu: { id: MenuId.ViewTitle, @@ -553,13 +551,15 @@ export class RefreshTestsAction extends Action2 { id: RefreshTestsAction.ID, title: localize('testing.refresh', "Refresh Tests"), category, - f1: true, - menu: { + menu: [{ id: MenuId.ViewTitle, order: ActionOrder.Refresh, group: 'refresh', when: ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId) - } + }, { + id: MenuId.CommandPalette, + when: TestingContextKeys.providerCount.isEqualTo(true), + }], }); } @@ -578,11 +578,13 @@ export class ClearTestResultsAction extends Action2 { id: ClearTestResultsAction.ID, title: localize('testing.clearResults', "Clear All Results"), category, - f1: true, icon: Codicon.trash, - menu: { + menu: [{ id: MenuId.TestPeekTitle, - }, + }, { + id: MenuId.CommandPalette, + when: TestingContextKeys.hasAnyResults.isEqualTo(true), + },], }); } @@ -600,7 +602,6 @@ export class GoToTest extends Action2 { super({ id: GoToTest.ID, title: localize('testing.editFocusedTest', "Go to Test"), - f1: false, menu: { id: MenuId.TestItem, when: TestingContextKeys.testItemHasUri.isEqualTo(true), @@ -709,7 +710,6 @@ abstract class ToggleAutoRun extends Action2 { super({ id: ToggleAutoRun.ID, title, - f1: true, icon: icons.testingAutorunIcon, toggled: whenToggleIs === true ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE, menu: { @@ -746,6 +746,16 @@ export class AutoRunOffAction extends ToggleAutoRun { abstract class RunOrDebugAtCursor extends Action2 { + constructor(options: IAction2Options) { + super({ + ...options, + menu: { + id: MenuId.CommandPalette, + when: hasAnyTestProvider, + }, + }); + } + /** * @override */ @@ -801,7 +811,6 @@ export class RunAtCursor extends RunOrDebugAtCursor { super({ id: RunAtCursor.ID, title: localize('testing.runAtCursor', "Run Test at Cursor"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -829,7 +838,6 @@ export class DebugAtCursor extends RunOrDebugAtCursor { super({ id: DebugAtCursor.ID, title: localize('testing.debugAtCursor', "Debug Test at Cursor"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -852,6 +860,16 @@ export class DebugAtCursor extends RunOrDebugAtCursor { } abstract class RunOrDebugCurrentFile extends Action2 { + constructor(options: IAction2Options) { + super({ + ...options, + menu: { + id: MenuId.CommandPalette, + when: hasAnyTestProvider, + }, + }); + } + /** * @override */ @@ -893,13 +911,16 @@ export class RunCurrentFile extends RunOrDebugCurrentFile { super({ id: RunCurrentFile.ID, title: localize('testing.runCurrentFile', "Run Tests in Current File"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.US_SEMICOLON, KeyCode.KEY_F), }, + menu: { + id: MenuId.CommandPalette, + when: hasAnyTestProvider, + }, }); } @@ -921,7 +942,6 @@ export class DebugCurrentFile extends RunOrDebugCurrentFile { super({ id: DebugCurrentFile.ID, title: localize('testing.debugCurrentFile', "Debug Tests in Current File"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -984,6 +1004,15 @@ abstract class RunOrDebugExtsByPath extends Action2 { } abstract class RunOrDebugFailedTests extends RunOrDebugExtsByPath { + constructor(options: IAction2Options) { + super({ + ...options, + menu: { + id: MenuId.CommandPalette, + when: hasAnyTestProvider, + }, + }); + } /** * @inheritdoc */ @@ -1008,6 +1037,19 @@ abstract class RunOrDebugFailedTests extends RunOrDebugExtsByPath { } abstract class RunOrDebugLastRun extends RunOrDebugExtsByPath { + constructor(options: IAction2Options) { + super({ + ...options, + menu: { + id: MenuId.CommandPalette, + when: ContextKeyAndExpr.create([ + hasAnyTestProvider, + TestingContextKeys.hasAnyResults.isEqualTo(true), + ]), + }, + }); + } + /** * @inheritdoc */ @@ -1032,7 +1074,6 @@ export class ReRunFailedTests extends RunOrDebugFailedTests { super({ id: ReRunFailedTests.ID, title: localize('testing.reRunFailTests', "Rerun Failed Tests"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -1059,7 +1100,6 @@ export class DebugFailedTests extends RunOrDebugFailedTests { super({ id: DebugFailedTests.ID, title: localize('testing.debugFailTests', "Debug Failed Tests"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -1086,7 +1126,6 @@ export class ReRunLastRun extends RunOrDebugLastRun { super({ id: ReRunLastRun.ID, title: localize('testing.reRunLastRun', "Rerun Last Run"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -1113,7 +1152,6 @@ export class DebugLastRun extends RunOrDebugLastRun { super({ id: DebugLastRun.ID, title: localize('testing.debugLastRun', "Debug Last Run"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -1140,7 +1178,6 @@ export class SearchForTestExtension extends Action2 { super({ id: SearchForTestExtension.ID, title: localize('testing.searchForTestExtension', "Search for Test Extension"), - f1: false, }); } @@ -1158,12 +1195,15 @@ export class OpenOutputPeek extends Action2 { super({ id: OpenOutputPeek.ID, title: localize('testing.openOutputPeek', "Peek Output"), - f1: true, category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.US_SEMICOLON, KeyCode.KEY_M), }, + menu: { + id: MenuId.CommandPalette, + when: TestingContextKeys.hasAnyResults.isEqualTo(true), + }, }); } diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index ea4070e565f..688c80c38c0 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -95,6 +95,7 @@ export class TestResultService implements ITestResultService { public readonly onTestChanged = this.testChangeEmitter.event; private readonly isRunning: IContextKey; + private readonly hasAnyResults: IContextKey; private readonly loadResults = once(() => this.storage.read().then(loaded => { for (let i = loaded.length - 1; i >= 0; i--) { this.push(loaded[i]); @@ -108,6 +109,7 @@ export class TestResultService implements ITestResultService { @ITestResultStorage private readonly storage: ITestResultStorage, ) { this.isRunning = TestingContextKeys.isRunning.bindTo(contextKeyService); + this.hasAnyResults = TestingContextKeys.hasAnyResults.bindTo(contextKeyService); } /** @@ -148,6 +150,7 @@ export class TestResultService implements ITestResultService { this.persistScheduler.schedule(); } + this.hasAnyResults.set(true); if (this.results.length > RETAIN_MAX_RESULTS) { this.results.pop(); } @@ -200,6 +203,7 @@ export class TestResultService implements ITestResultService { this._results = keep; this.persistScheduler.schedule(); + this.hasAnyResults.set(false); this.changeResultEmitter.fire({ removed }); } diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index b373dc35c01..b9bfb497595 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -12,6 +12,7 @@ export namespace TestingContextKeys { export const providerCount = new RawContextKey('testing.providerCount', 0); export const hasDebuggableTests = new RawContextKey('testing.hasDebuggableTests', false); export const hasRunnableTests = new RawContextKey('testing.hasRunnableTests', false); + export const hasAnyResults = new RawContextKey('testing.hasAnyResults', false); export const viewMode = new RawContextKey('testing.explorerViewMode', TestExplorerViewMode.List); export const viewSorting = new RawContextKey('testing.explorerViewSorting', TestExplorerViewSorting.ByLocation); export const isRunning = new RawContextKey('testing.isRunning', false); From 77b7a91b4cb12fe8f618cb55657ccc14278838c9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 9 Jun 2021 12:49:43 -0700 Subject: [PATCH 26/35] testing: allow running tests from RHS of diff editor Fixes #124493 --- .../workbench/contrib/testing/browser/testingDecorations.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index a110688e378..f18c8fd63ed 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -37,11 +37,11 @@ import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/t import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { IMainThreadTestCollection, ITestService } from 'vs/workbench/contrib/testing/common/testService'; -function isInDiffEditor(codeEditorService: ICodeEditorService, codeEditor: ICodeEditor): boolean { +function isOriginalInDiffEditor(codeEditorService: ICodeEditorService, codeEditor: ICodeEditor): boolean { const diffEditors = codeEditorService.listDiffEditors(); for (const diffEditor of diffEditors) { - if (diffEditor.getModifiedEditor() === codeEditor || diffEditor.getOriginalEditor() === codeEditor) { + if (diffEditor.getOriginalEditor() === codeEditor) { return true; } } @@ -130,7 +130,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio } private attachModel(uri?: URI) { - if (isInDiffEditor(this.codeEditorService, this.editor)) { + if (isOriginalInDiffEditor(this.codeEditorService, this.editor)) { uri = undefined; } From ef6ec74920b7b7e39bab652f49a2249926bd5ce6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 9 Jun 2021 22:21:13 +0200 Subject: [PATCH 27/35] Ensure hover gets disposed when icon label is removed --- .../browser/ui/iconLabel/iconLabelHover.ts | 137 +++++++++--------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts b/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts index a80a53e02bb..8d14e2c311b 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts @@ -6,13 +6,13 @@ import { isFunction, isString } from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { DomEmitter } from 'vs/base/browser/event'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { localize } from 'vs/nls'; import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { RunOnceScheduler } from 'vs/base/common/async'; export function setupNativeHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void { @@ -32,79 +32,90 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM const tooltip = getTooltipForCustom(markdownTooltip); - let hoverOptions: IHoverDelegateOptions | undefined; - let mouseX: number | undefined; - let isHovering = false; - let tokenSource: CancellationTokenSource; - let hoverDisposable: IDisposable | undefined; + let hoverPreparation: IDisposable | undefined; - const mouseOverDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_OVER, true); - mouseOverDomEmitter.event((e: MouseEvent) => { - if (isHovering) { + let hoverWidget: IDisposable | undefined; + + const mouseEnter = (e: MouseEvent) => { + if (hoverPreparation) { return; } - tokenSource = new CancellationTokenSource(); - function mouseLeaveOrDown(e: MouseEvent): void { + + const tokenSource = new CancellationTokenSource(); + + const mouseLeaveOrDown = (e: MouseEvent) => { const isMouseDown = e.type === dom.EventType.MOUSE_DOWN; if (isMouseDown) { - hoverDisposable?.dispose(); - hoverDisposable = undefined; + hoverWidget?.dispose(); + hoverWidget = undefined; } if (isMouseDown || (e).fromElement === htmlElement) { - isHovering = false; - hoverOptions = undefined; - tokenSource.dispose(true); - mouseLeaveDomEmitter.dispose(); - mouseDownDomEmitter.dispose(); + hoverPreparation?.dispose(); + hoverPreparation = undefined; } - } - const mouseLeaveDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_LEAVE, true); - mouseLeaveDomEmitter.event(mouseLeaveOrDown); - const mouseDownDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_DOWN, true); - mouseDownDomEmitter.event(mouseLeaveOrDown); - isHovering = true; + }; + const mouseLeaveDomListener = dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_LEAVE, mouseLeaveOrDown, true); + const mouseDownDownListener = dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_DOWN, mouseLeaveOrDown, true); - function mouseMove(e: MouseEvent): void { - mouseX = e.x; + const target: IHoverDelegateTarget = { + targetElements: [htmlElement], + dispose: () => { } + }; + + let mouseMoveDomListener: IDisposable | undefined; + if (hoverDelegate.placement === undefined || hoverDelegate.placement === 'mouse') { + const mouseMove = (e: MouseEvent) => target.x = e.x + 10; + mouseMoveDomListener = dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_MOVE, mouseMove, true); } - const mouseMoveDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_MOVE, true); - mouseMoveDomEmitter.event(mouseMove); - setTimeout(async () => { - if (isHovering && tooltip) { - // Re-use the already computed hover options if they exist. - if (!hoverOptions) { - const target: IHoverDelegateTarget = { - targetElements: [htmlElement], - dispose: () => { } - }; - hoverOptions = { - text: localize('iconLabel.loading', "Loading..."), + + const showHover = async () => { + if (hoverPreparation) { + + const hoverOptions = { + text: localize('iconLabel.loading', "Loading..."), + target, + hoverPosition: HoverPosition.BELOW + }; + hoverWidget?.dispose(); + hoverWidget = hoverDelegate.showHover(hoverOptions); + + const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined); + + hoverWidget?.dispose(); + hoverWidget = undefined; + + // awaiting the tooltip could take a while. Make sure we're still preparing to hover. + if (resolvedTooltip && hoverPreparation) { + const hoverOptions = { + text: resolvedTooltip, target, + showPointer: hoverDelegate.placement === 'element', hoverPosition: HoverPosition.BELOW }; - hoverDisposable = adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering); - const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined); - if (resolvedTooltip) { - hoverOptions = { - text: resolvedTooltip, - target, - showPointer: hoverDelegate.placement === 'element', - hoverPosition: HoverPosition.BELOW - }; - // awaiting the tooltip could take a while. Make sure we're still hovering. - hoverDisposable = adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering); - } else if (hoverDisposable) { - hoverDisposable.dispose(); - hoverDisposable = undefined; - } + hoverWidget = hoverDelegate.showHover(hoverOptions); } } - mouseMoveDomEmitter.dispose(); - }, hoverDelegate.delay); + mouseMoveDomListener?.dispose(); + }; + const timeout = new RunOnceScheduler(showHover, hoverDelegate.delay); + timeout.schedule(); + + hoverPreparation = toDisposable(() => { + timeout.dispose(); + mouseMoveDomListener?.dispose(); + mouseDownDownListener.dispose(); + mouseLeaveDomListener.dispose(); + tokenSource.dispose(true); + }); + }; + const mouseOverDomEmitter = dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_OVER, mouseEnter, true); + return toDisposable(() => { + mouseOverDomEmitter.dispose(); + hoverPreparation?.dispose(); + hoverWidget?.dispose(); }); - return mouseOverDomEmitter; } @@ -118,13 +129,3 @@ function getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString) return async () => markdown; } } - -function adjustXAndShowCustomHover(hoverOptions: IHoverDelegateOptions | undefined, mouseX: number | undefined, hoverDelegate: IHoverDelegate, isHovering: boolean): IDisposable | undefined { - if (hoverOptions && isHovering) { - if (mouseX !== undefined && (hoverDelegate.placement === undefined || hoverDelegate.placement === 'mouse')) { - (hoverOptions.target).x = mouseX + 10; - } - return hoverDelegate.showHover(hoverOptions); - } - return undefined; -} From f163b54a19c107962cc04b65c7d9caae893005f7 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 9 Jun 2021 22:31:05 +0200 Subject: [PATCH 28/35] Fixes #123882 The catch block around the return statement was never executing, as it would catch only synchronous errors thrown by `_doHandleExtensionTests`. By awaiting the returned promise, we make sure to catch also promise rejections, which gives us a chance to log the error message on the extension host console as well. --- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index abb761d9dbe..e927e9afa65 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -551,7 +551,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme public async $extensionTestsExecute(): Promise { await this._eagerExtensionsActivated.wait(); try { - return this._doHandleExtensionTests(); + return await this._doHandleExtensionTests(); } catch (error) { console.error(error); // ensure any error message makes it onto the console throw error; From 74d24ff55f41edd3744a1c4983a56d0ba5998936 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 9 Jun 2021 22:37:43 +0200 Subject: [PATCH 29/35] Reduce impact of fix for #125303 to standalone editor only --- src/vs/base/browser/ui/aria/aria.css | 3 +-- src/vs/editor/standalone/browser/standalone-tokens.css | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/aria/aria.css b/src/vs/base/browser/ui/aria/aria.css index af06e896bd5..fdcbb34c7d7 100644 --- a/src/vs/base/browser/ui/aria/aria.css +++ b/src/vs/base/browser/ui/aria/aria.css @@ -6,5 +6,4 @@ .monaco-aria-container { position: absolute; /* try to hide from window but not from screen readers */ left:-999em; - top: 0; /* avoid being placed underneath a sibling element */ -} +} \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/standalone-tokens.css b/src/vs/editor/standalone/browser/standalone-tokens.css index 5e6bbadb732..1933d35edae 100644 --- a/src/vs/editor/standalone/browser/standalone-tokens.css +++ b/src/vs/editor/standalone/browser/standalone-tokens.css @@ -26,6 +26,7 @@ /* See https://github.com/microsoft/monaco-editor/issues/2168#issuecomment-780078600 */ .monaco-aria-container { position: absolute !important; + top: 0; /* avoid being placed underneath a sibling element */ height: 1px; width: 1px; margin: -1px; From daec68cd66f3c537152ad11c63e8c33b5da297cc Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 9 Jun 2021 23:41:52 +0200 Subject: [PATCH 30/35] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 15120a28b2a..69c6e472a98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.58.0", - "distro": "3f43781e788d694f33746808cc5182852222f935", + "distro": "f9acd3d731fc630bf6b665d551d5fa12ce0560e6", "author": { "name": "Microsoft Corporation" }, From a88fa5b58f7478216d31f335339a23e0e63511e5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 9 Jun 2021 08:01:31 -0700 Subject: [PATCH 31/35] Format terminalMenus Part of #123473 --- .../contrib/terminal/browser/terminalMenus.ts | 87 ++++++++++++------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index e00ba01073e..069593910e7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -35,7 +35,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.MenubarTerminalMenu, item: { + id: MenuId.MenubarTerminalMenu, + item: { group: TerminalMenuBarGroup.Create, command: { id: TerminalCommandId.New, @@ -45,7 +46,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.MenubarTerminalMenu, item: { + id: MenuId.MenubarTerminalMenu, + item: { group: TerminalMenuBarGroup.Create, command: { id: TerminalCommandId.Split, @@ -57,7 +59,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.MenubarTerminalMenu, item: { + id: MenuId.MenubarTerminalMenu, + item: { group: TerminalMenuBarGroup.Run, command: { id: TerminalCommandId.RunActiveFile, @@ -68,7 +71,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.MenubarTerminalMenu, item: { + id: MenuId.MenubarTerminalMenu, + item: { group: TerminalMenuBarGroup.Run, command: { id: TerminalCommandId.RunSelectedText, @@ -84,7 +88,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { group: ContextMenuGroup.Create, command: { id: TerminalCommandId.Split, @@ -93,7 +98,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.New, title: localize('workbench.action.terminal.new.short', "New Terminal") @@ -102,7 +108,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.Kill, title: terminalStrings.kill.value @@ -111,7 +118,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.CopySelection, title: localize('workbench.action.terminal.copySelection.short', "Copy") @@ -121,7 +129,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.Paste, title: localize('workbench.action.terminal.paste.short', "Paste") @@ -131,7 +140,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.Clear, title: localize('workbench.action.terminal.clear', "Clear") @@ -140,7 +150,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.ShowTabs, title: localize('workbench.action.terminal.showsTabs', "Show Tabs") @@ -150,7 +161,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInstanceContext, item: { + id: MenuId.TerminalInstanceContext, + item: { command: { id: TerminalCommandId.SelectAll, title: localize('workbench.action.terminal.selectAll', "Select All"), @@ -165,7 +177,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.TerminalTabEmptyAreaContext, item: { + id: MenuId.TerminalTabEmptyAreaContext, + item: { command: { id: TerminalCommandId.NewWithProfile, title: localize('workbench.action.terminal.newWithProfile.short', "New Terminal With Profile") @@ -174,7 +187,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabEmptyAreaContext, item: { + id: MenuId.TerminalTabEmptyAreaContext, + item: { command: { id: TerminalCommandId.New, title: localize('workbench.action.terminal.new.short', "New Terminal") @@ -188,7 +202,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.TerminalNewDropdownContext, item: { + id: MenuId.TerminalNewDropdownContext, + item: { command: { id: TerminalCommandId.SelectDefaultProfile, title: { value: localize('workbench.action.terminal.selectDefaultProfile', "Select Default Profile"), original: 'Select Default Profile' } @@ -197,7 +212,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalNewDropdownContext, item: { + id: MenuId.TerminalNewDropdownContext, + item: { command: { id: TerminalCommandId.ConfigureTerminalSettings, title: localize('workbench.action.terminal.openSettings', "Configure Terminal Settings") @@ -211,7 +227,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.ViewTitle, item: { + id: MenuId.ViewTitle, + item: { command: { id: TerminalCommandId.SwitchTerminal, title: { value: localize('workbench.action.terminal.switchTerminal', "Switch Terminal"), original: 'Switch Terminal' } @@ -230,7 +247,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.TerminalInlineTabContext, item: { + id: MenuId.TerminalInlineTabContext, + item: { group: ContextMenuGroup.Create, command: { id: TerminalCommandId.Split, @@ -239,7 +257,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInlineTabContext, item: { + id: MenuId.TerminalInlineTabContext, + item: { command: { id: TerminalCommandId.ChangeIcon, title: localize('workbench.action.terminal.changeIcon', "Change Icon...") @@ -249,7 +268,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInlineTabContext, item: { + id: MenuId.TerminalInlineTabContext, + item: { command: { id: TerminalCommandId.ChangeColor, title: localize('workbench.action.terminal.changeColor', "Change Color...") @@ -259,7 +279,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInlineTabContext, item: { + id: MenuId.TerminalInlineTabContext, + item: { command: { id: TerminalCommandId.Rename, title: localize('workbench.action.terminal.rename', "Rename...") @@ -268,7 +289,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalInlineTabContext, item: { + id: MenuId.TerminalInlineTabContext, + item: { command: { id: TerminalCommandId.Kill, title: terminalStrings.kill.value @@ -282,7 +304,8 @@ export function setupTerminalMenus(): void { MenuRegistry.appendMenuItems( [ { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { command: { id: TerminalCommandId.SplitInstance, title: terminalStrings.split.value, @@ -291,7 +314,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { command: { id: TerminalCommandId.RenameInstance, title: localize('workbench.action.terminal.renameInstance', "Rename...") @@ -300,7 +324,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { command: { id: TerminalCommandId.ChangeIconInstance, title: localize('workbench.action.terminal.changeIcon', "Change Icon...") @@ -309,7 +334,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { command: { id: TerminalCommandId.ChangeColorInstance, title: localize('workbench.action.terminal.changeColor', "Change Color...") @@ -318,7 +344,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { group: ContextMenuGroup.Config, command: { id: TerminalCommandId.JoinInstance, @@ -328,7 +355,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { group: ContextMenuGroup.Config, command: { id: TerminalCommandId.UnsplitInstance, @@ -338,7 +366,8 @@ export function setupTerminalMenus(): void { } }, { - id: MenuId.TerminalTabContext, item: { + id: MenuId.TerminalTabContext, + item: { command: { id: TerminalCommandId.KillInstance, title: terminalStrings.kill.value From 398427543036a23e2c15d39daf903570127ebbbf Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:12:29 -0700 Subject: [PATCH 32/35] Fix localized command string Fixes #125786 --- .../contrib/terminal/common/terminalConfiguration.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 19b41687764..33523697796 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -248,7 +248,12 @@ const terminalConfiguration: IConfigurationNode = { default: false }, [TerminalSettingId.CommandsToSkipShell]: { - markdownDescription: localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by VS Code. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n \n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n \n\nThe following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, [open the default settings JSON](command:workbench.action.openRawDefaultSettings 'Open Default Settings (JSON)') and search for the first command from the list below.\n\n \n\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), + markdownDescription: localize( + 'terminal.integrated.commandsToSkipShell', + "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by VS Code. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n \n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n \n\nThe following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, {1} and search for the first command from the list below.\n\n \n\nDefault Skipped Commands:\n\n{0}", + DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n'), + `[${localize('openDefaultSettingsJson', "open the default settings JSON")}](command:workbench.action.openRawDefaultSettings '${localize('openDefaultSettingsJson.capitalized', "Open Default Settings (JSON)")}')` + ), type: 'array', items: { type: 'string' From 52ea1fd44eaf918600ce2c8d49bca598aefa0fb6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 12:13:46 -0700 Subject: [PATCH 33/35] Move toggleDragDropEnabled --- .../browser/view/renderers/webviewPreloads.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index cd64b942812..f595c7840d8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -792,12 +792,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re break; case 'notebookOptions': currentOptions = event.data.options; - - // Update markup cells - for (const markdownContainer of document.querySelectorAll('.preview')) { - setMarkupContainerDraggable(markdownContainer, currentOptions.dragAndDropEnabled); - } - + notebookDocument.toggleDragDropEnabled(currentOptions.dragAndDropEnabled); break; } }); @@ -1076,6 +1071,12 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re cell.setSelected(selectedCellSet.has(cell.id)); } } + + public toggleDragDropEnabled(dragAndDropEnabled: boolean) { + for (const cell of this._markupCells.values()) { + cell.toggleDragDropEnabled(dragAndDropEnabled); + } + } }(); class MarkupCell implements IOutputItem { @@ -1100,6 +1101,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re this.element.classList.add('preview'); this.element.style.position = 'absolute'; this.element.style.top = top + 'px'; + this.toggleDragDropEnabled(currentOptions.dragAndDropEnabled); root.appendChild(this.element); this.addEventListeners(); @@ -1158,8 +1160,6 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re postNotebookMessage('mouseLeaveMarkupCell', { cellId: this.id }); }); - setMarkupContainerDraggable(this.element, currentOptions.dragAndDropEnabled); - this.element.addEventListener('dragstart', e => { markupCellDragManager.startDrag(e, this.id); }); @@ -1226,6 +1226,16 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re public setSelected(selected: boolean) { this.element.classList.toggle('selected', selected); } + + public toggleDragDropEnabled(enabled: boolean) { + if (enabled) { + this.element.classList.add('draggable'); + this.element.setAttribute('draggable', 'true'); + } else { + this.element.classList.remove('draggable'); + this.element.removeAttribute('draggable'); + } + } } vscode.postMessage({ @@ -1233,16 +1243,6 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re type: 'initialized' }); - function setMarkupContainerDraggable(element: Element, isDraggable: boolean) { - if (isDraggable) { - element.classList.add('draggable'); - element.setAttribute('draggable', 'true'); - } else { - element.classList.remove('draggable'); - element.removeAttribute('draggable'); - } - } - function postNotebookMessage( type: T['type'], properties: Omit @@ -1254,7 +1254,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re }); } - const markupCellDragManager = new class MarkdownPreviewDragManager { + const markupCellDragManager = new class MarkupCellDragManager { private currentDrag: { cellId: string, clientY: number } | undefined; From bc14fe9e8d5e99db500dca9713756167f1cda1d7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jun 2021 15:15:48 -0700 Subject: [PATCH 34/35] Use templates for markdown styles These styles shouldn't be added into the notebook itself, only into the shadow dom of the markdown cells --- .../markdown-language-features/notebook/index.ts | 14 ++++++++++---- extensions/markdown-math/notebook/katex.ts | 10 +++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index 4dc45856e45..699bd2129ad 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -11,7 +11,6 @@ export function activate() { }); const style = document.createElement('style'); - style.classList.add('markdown-style'); style.textContent = ` .emptyMarkdownCell::before { content: "${document.documentElement.style.getPropertyValue('--notebook-cell-markup-empty-content')}"; @@ -134,7 +133,10 @@ export function activate() { white-space: pre-wrap; } `; - document.head.append(style); + const template = document.createElement('template'); + template.classList.add('markdown-style'); + template.content.appendChild(style); + document.head.appendChild(template); return { renderOutputItem: (outputInfo: { text(): string }, element: HTMLElement) => { @@ -148,8 +150,12 @@ export function activate() { previewRoot.appendChild(defaultStyles.cloneNode(true)); // And then contributed styles - for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) { - previewRoot.appendChild(markdownStyleNode.cloneNode(true)); + for (const element of document.getElementsByClassName('markdown-style')) { + if (element instanceof HTMLTemplateElement) { + previewRoot.appendChild(element.content.cloneNode(true)); + } else { + previewRoot.appendChild(element.cloneNode(true)); + } } previewNode = document.createElement('div'); diff --git a/extensions/markdown-math/notebook/katex.ts b/extensions/markdown-math/notebook/katex.ts index 27d7be58be7..0753199edad 100644 --- a/extensions/markdown-math/notebook/katex.ts +++ b/extensions/markdown-math/notebook/katex.ts @@ -18,16 +18,20 @@ export async function activate(ctx: { link.rel = 'stylesheet'; link.classList.add('markdown-style'); link.href = styleHref; - document.head.append(link); const style = document.createElement('style'); - style.classList.add('markdown-style'); style.textContent = ` .katex-error { color: var(--vscode-editorError-foreground); } `; - document.head.append(style); + + // Put Everything into a template + const styleTemplate = document.createElement('template'); + styleTemplate.classList.add('markdown-style'); + styleTemplate.content.appendChild(style); + styleTemplate.content.appendChild(link); + document.head.appendChild(styleTemplate); const katex = require('@iktakahiro/markdown-it-katex'); markdownItRenderer.extendMarkdownIt((md: markdownIt.MarkdownIt) => { From ff3e30b932dcd688fb8637cd1cdca14c0112b4d3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:24:03 -0700 Subject: [PATCH 35/35] Move remaining terminal menu registrations to terminalMenus Fixes #123473 --- .../terminal/browser/terminalActions.ts | 68 ++-------------- .../contrib/terminal/browser/terminalMenus.ts | 81 +++++++++++++++++++ .../terminal/common/terminalStrings.ts | 4 + 3 files changed, 92 insertions(+), 61 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 53ca5cc50c7..8c3944f90b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -15,10 +15,10 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { EndOfLinePreference } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; -import { Action2, ICommandActionTitle, ILocalizedString, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, ICommandActionTitle, ILocalizedString, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyAndExpr, ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -32,7 +32,7 @@ import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/w import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; -import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TerminalCommandId, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; @@ -343,38 +343,10 @@ export function registerTerminalActions() { constructor() { super({ id: TerminalCommandId.Focus, - title: { value: localize('workbench.action.terminal.focus', "Focus Terminal"), original: 'Focus Terminal' }, + title: terminalStrings.focus, f1: true, category, - precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, - // This command is used to show instead of tabs when there is only a single terminal - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - order: 0, - when: ContextKeyAndExpr.create([ - ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID), - ContextKeyExpr.has(`config.${TerminalSettingId.TabsEnabled}`), - ContextKeyExpr.or( - ContextKeyExpr.and( - ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'singleTerminal'), - ContextKeyExpr.equals('terminalCount', 1) - ), - ContextKeyExpr.and( - ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'singleTerminalOrNarrow'), - ContextKeyExpr.or( - ContextKeyExpr.equals('terminalCount', 1), - ContextKeyExpr.has('isTerminalTabsNarrow') - ) - ), - ContextKeyExpr.and( - ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'singleGroup'), - ContextKeyExpr.equals('terminalGroupCount', 1) - ), - ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'always') - ) - ]), - } + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { @@ -1362,15 +1334,6 @@ export function registerTerminalActions() { type: 'object' } }] - }, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - order: 2, - when: ContextKeyAndExpr.create([ - ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID), - ContextKeyExpr.not(`config.${TerminalSettingId.TabsEnabled}`) - ]) } }); } @@ -1579,16 +1542,7 @@ export function registerTerminalActions() { f1: true, category, precondition: ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN), - icon: Codicon.trash, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - order: 3, - when: ContextKeyAndExpr.create([ - ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID), - ContextKeyExpr.not(`config.${TerminalSettingId.TabsEnabled}`) - ]) - } + icon: Codicon.trash }); } async run(accessor: ServicesAccessor) { @@ -1683,15 +1637,7 @@ export function registerTerminalActions() { title: TerminalCommandId.CreateWithProfileButton, f1: false, category, - precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, - menu: [{ - id: MenuId.ViewTitle, - group: 'navigation', - order: 0, - when: ContextKeyAndExpr.create([ - ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID) - ]), - }] + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index 069593910e7..659322dfb40 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Codicon } from 'vs/base/common/codicons'; import { localize } from 'vs/nls'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyAndExpr, ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -240,6 +241,86 @@ export function setupTerminalMenus(): void { ContextKeyExpr.not(`config.${TerminalSettingId.TabsEnabled}`) ]), } + }, + { + // This is used to show instead of tabs when there is only a single terminal + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.Focus, + title: terminalStrings.focus + }, + group: 'navigation', + order: 0, + when: ContextKeyAndExpr.create([ + ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID), + ContextKeyExpr.has(`config.${TerminalSettingId.TabsEnabled}`), + ContextKeyExpr.or( + ContextKeyExpr.and( + ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'singleTerminal'), + ContextKeyExpr.equals('terminalCount', 1) + ), + ContextKeyExpr.and( + ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'singleTerminalOrNarrow'), + ContextKeyExpr.or( + ContextKeyExpr.equals('terminalCount', 1), + ContextKeyExpr.has('isTerminalTabsNarrow') + ) + ), + ContextKeyExpr.and( + ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'singleGroup'), + ContextKeyExpr.equals('terminalGroupCount', 1) + ), + ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActiveTerminal}`, 'always') + ) + ]), + } + }, + { + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.Split, + title: terminalStrings.split, + icon: Codicon.splitHorizontal + }, + group: 'navigation', + order: 2, + when: ContextKeyAndExpr.create([ + ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID), + ContextKeyExpr.not(`config.${TerminalSettingId.TabsEnabled}`) + ]) + } + }, + { + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.Kill, + title: terminalStrings.kill, + icon: Codicon.trash + }, + group: 'navigation', + order: 3, + when: ContextKeyAndExpr.create([ + ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID), + ContextKeyExpr.not(`config.${TerminalSettingId.TabsEnabled}`) + ]) + } + }, + { + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.CreateWithProfileButton, + title: TerminalCommandId.CreateWithProfileButton + }, + group: 'navigation', + order: 0, + when: ContextKeyAndExpr.create([ + ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID) + ]) + } } ] ); diff --git a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts index fff953de11b..854fb806931 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts @@ -17,6 +17,10 @@ export function formatMessageForTerminal(message: string, excludeLeadingNewLine: * An object holding strings shared by multiple parts of the terminal */ export const terminalStrings = { + focus: { + value: localize('workbench.action.terminal.focus', "Focus Terminal"), + original: 'Focus Terminal' + }, kill: { value: localize('killTerminal', "Kill Terminal"), original: 'Kill Terminal',