From 67ad033d53289a13253b62173f63b2a69229556d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Jun 2018 16:00:03 +0200 Subject: [PATCH 001/151] hello breadcrumbs widget --- .../electron-browser/breadcrumbsWidget.css | 32 ++++ .../electron-browser/breadcrumbsWidget.ts | 171 ++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css create mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css new file mode 100644 index 00000000000..2901388a828 --- /dev/null +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-breadcrumbs { + user-select: none; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-start; + --item-hover-background: green; + --item-hover-color: inhert; +} + +.monaco-breadcrumbs .monaco-breadcrumb-item { + display: inline-block; + padding: 0 5px 0 5px; + flex: 0 1 auto; + white-space: nowrap; + cursor: pointer; +} + +.monaco-breadcrumbs .monaco-breadcrumb-item:hover { + color: var(--item-hover-color); + background-color: var(--item-hover-background); +} + +.monaco-breadcrumbs .monaco-breadcrumb-item-more::after { + content: '〉'; + padding-left: 5px; +} diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts new file mode 100644 index 00000000000..3c23a030b22 --- /dev/null +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./breadcrumbsWidget'; +import * as dom from 'vs/base/browser/dom'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Event, Emitter } from 'vs/base/common/event'; + +export class BreadcrumbsItem { + + constructor( + readonly node: HTMLElement, + readonly more: boolean + ) { + + } + + dispose(): void { + // + } +} + +export class SimpleBreadcrumbsItem extends BreadcrumbsItem { + + constructor(text: string, title: string = text, more: boolean = true) { + super(document.createElement('span'), more); + this.node.innerText = text; + this.node.title = title; + } +} + +export class RenderedBreadcrumbsItem extends BreadcrumbsItem { + + + private _disposables: IDisposable[] = []; + + constructor(render: (element: E, container: HTMLSpanElement, bucket: IDisposable[]) => any, element: E, more: boolean) { + super(document.createElement('span'), more); + render(element, this.node, this._disposables); + } + + dispose() { + dispose(this._disposables); + super.dispose(); + } +} + +export class BreadcrumbsWidget { + + private readonly _disposables = new Array(); + private readonly _domNode: HTMLSpanElement; + private readonly _scrollable: DomScrollableElement; + private _cachedWidth: number; + + private readonly _onDidSelectItem = new Emitter(); + readonly onDidSelectItem: Event = this._onDidSelectItem.event; + + private readonly _items = new Array(); + private readonly _nodes = new Array(); + private readonly _freeNodes = new Array(); + private _activeItem: number; + + constructor( + container: HTMLElement + ) { + this._domNode = document.createElement('span'); + this._domNode.className = 'monaco-breadcrumbs'; + this._scrollable = new DomScrollableElement(this._domNode, { + vertical: ScrollbarVisibility.Hidden, + horizontal: ScrollbarVisibility.Auto, + horizontalScrollbarSize: 3, + useShadows: false + }); + this._disposables.push(this._scrollable); + this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); + container.appendChild(this._scrollable.getDomNode()); + } + + dispose(): void { + dispose(this._disposables); + this._domNode.remove(); + this._disposables.length = 0; + this._nodes.length = 0; + this._freeNodes.length = 0; + } + + layout(width: number = this._cachedWidth): void { + if (typeof width === 'number') { + this._cachedWidth = width; + this._domNode.style.width = `${this._cachedWidth}px`; + this._scrollable.scanDomNode(); + } + } + + focus(): void { + this._domNode.focus(); + } + + select(nth: number): void { + if (typeof this._activeItem === 'number') { + dom.removeClass(this._nodes[this._activeItem], 'active'); + } + if (nth >= this._nodes.length) { + this._activeItem = nth; + let node = this._nodes[this._activeItem]; + dom.addClass(node, 'active'); + this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); + } + } + + append(item: BreadcrumbsItem): void { + this._items.push(item); + this._render(this._items.length - 1); + } + + replace(existing: BreadcrumbsItem, newItems: BreadcrumbsItem[]): void { + let start = !existing ? 0 : this._items.indexOf(existing); + let removed = this._items.splice(start, this._items.length - start, ...newItems); + this._render(start); + dispose(removed); + } + + private _render(start: number): void { + for (; start < this._items.length && start < this._nodes.length; start++) { + let item = this._items[start]; + let node = this._nodes[start]; + this._renderItem(item, node); + } + // case a: more nodes -> remove them + for (; start < this._nodes.length; start++) { + this._nodes[start].remove(); + this._freeNodes.push(this._nodes[start]); + } + this._nodes.length = this._items.length; + + // case b: more items -> render them + for (; start < this._items.length; start++) { + let item = this._items[start]; + let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('span'); + this._renderItem(item, node); + this._domNode.appendChild(node); + this._nodes[start] = node; + } + this.layout(); + this.select(this._nodes.length - 1); + } + + private _renderItem(item: BreadcrumbsItem, container: HTMLSpanElement): void { + dom.clearNode(container); + dom.append(container, item.node); + dom.addClass(container, 'monaco-breadcrumb-item'); + dom.toggleClass(container, 'monaco-breadcrumb-item-more', item.more); + } + + private _onClick(event: IMouseEvent): void { + for (let el = event.target; el; el = el.parentElement) { + let idx = this._nodes.indexOf(el as any); + if (idx >= 0) { + this._onDidSelectItem.fire(this._items[idx]); + break; + } + } + } +} From 83ca2647a0ff50d8abedf1f6789360b9e88b7d98 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Jun 2018 16:00:25 +0200 Subject: [PATCH 002/151] breadcrumbs fake integration --- .../breadcrumbs.contribution.ts | 14 +++ .../breadcrumbsStatusbarItem.ts | 89 +++++++++++++++++++ src/vs/workbench/workbench.main.ts | 2 + 3 files changed, 105 insertions(+) create mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts create mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts new file mode 100644 index 00000000000..a6cd82ad1ba --- /dev/null +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IStatusbarRegistry, StatusbarAlignment, StatusbarItemDescriptor } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { BreadcrumbsStatusbarItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem'; + +Registry.as(Extensions.Statusbar).registerStatusbarItem( + new StatusbarItemDescriptor(BreadcrumbsStatusbarItem, StatusbarAlignment.LEFT) +); diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts new file mode 100644 index 00000000000..6b8763df1f4 --- /dev/null +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { BreadcrumbsWidget, RenderedBreadcrumbsItem, BreadcrumbsItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { posix } from 'path'; +import URI from 'vs/base/common/uri'; +import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; + +export class BreadcrumbsStatusbarItem implements IStatusbarItem { + + private _widget: BreadcrumbsWidget; + private _disposables: IDisposable[] = []; + + constructor( + @IEditorService private readonly _editorService: IEditorService, + @IContextViewService private readonly _contextViewService: IContextViewService + ) { + } + + dispose(): void { + dispose(this._disposables); + } + + render(element: HTMLElement): IDisposable { + this._widget = new BreadcrumbsWidget(element); + this._widget.layout(300); + this._disposables.push(this._widget); + this._disposables.push(this._widget.onDidSelectItem(this._onDidSelectItem, this)); + this._disposables.push(this._editorService.onDidActiveEditorChange(this._onDidChangeActiveEditor, this)); + this._onDidChangeActiveEditor(); + return this; + } + + private _onDidChangeActiveEditor(): void { + let { activeEditor } = this._editorService; + if (!activeEditor) { + this._widget.replace(undefined, []); + return; + } + let resource = activeEditor.getResource(); + if (!resource) { + this._widget.replace(undefined, []); + return; + } + + interface Element { + name: string; + uri: URI; + } + + function render(element: Element, target: HTMLElement) { + target.innerText = getBaseLabel(element.uri); + target.title = getPathLabel(element.uri); + } + + let items: RenderedBreadcrumbsItem[] = []; + let path = resource.path; + while (path !== '/') { + let name = posix.basename(path); + let uri = resource.with({ path }); + path = posix.dirname(path); + items.unshift(new RenderedBreadcrumbsItem(render, { name, uri }, items.length !== 0)); + } + + this._widget.replace(undefined, items); + } + + private _onDidSelectItem(item: BreadcrumbsItem): void { + console.log(item, this._contextViewService._serviceBrand); + + // this._contextViewService.showContextView({ + // getAnchor() { + // return item.node; + // }, + // render(container) { + // container.innerText = JSON.stringify(item, undefined, 4); + // return null; + // } + // }); + } +} diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 4bb1eda080e..34cd6c0f235 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -94,6 +94,8 @@ import 'vs/workbench/electron-browser/workbench'; import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; +import 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution'; + import 'vs/workbench/parts/tasks/electron-browser/task.contribution'; import 'vs/workbench/parts/emmet/browser/emmet.browser.contribution'; From b7495d7c689f2808f3153a3f9bda8051af231358 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 10:02:07 +0200 Subject: [PATCH 003/151] adopt getPathLabel changes --- .../breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts index 6b8763df1f4..6d4d9929320 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts @@ -58,7 +58,7 @@ export class BreadcrumbsStatusbarItem implements IStatusbarItem { function render(element: Element, target: HTMLElement) { target.innerText = getBaseLabel(element.uri); - target.title = getPathLabel(element.uri); + target.title = getPathLabel(element.uri, undefined, undefined); } let items: RenderedBreadcrumbsItem[] = []; From c2c9e8dddcd564903ea0c2ba2e0acfffb82a1658 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 11:13:12 +0200 Subject: [PATCH 004/151] drop status bar playground, towards editor contrib --- .../breadcrumbs.contribution.ts | 26 +++- .../breadcrumbsStatusbarItem.ts | 89 ------------ .../electron-browser/breadcrumbsWidget.css | 9 +- .../electron-browser/breadcrumbsWidget.ts | 41 +++--- .../electron-browser/editorBreadcrumbs.ts | 128 ++++++++++++++++++ 5 files changed, 177 insertions(+), 116 deletions(-) delete mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts create mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts index a6cd82ad1ba..b0dc0ff39bb 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts @@ -5,10 +5,24 @@ 'use strict'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IStatusbarRegistry, StatusbarAlignment, StatusbarItemDescriptor } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { BreadcrumbsStatusbarItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { EditorBreadcrumbs } from 'vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -Registry.as(Extensions.Statusbar).registerStatusbarItem( - new StatusbarItemDescriptor(BreadcrumbsStatusbarItem, StatusbarAlignment.LEFT) -); +registerEditorContribution(EditorBreadcrumbs); + + +const BreadcrumbCommandCtor = EditorCommand.bindToContribution(EditorBreadcrumbs.get); + +registerEditorCommand(new BreadcrumbCommandCtor({ + id: 'breadcrumbs.focus', + precondition: undefined, + handler: x => x.focus(), + kbOpts: { + weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyCode.US_DOT + } +})); diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts deleted file mode 100644 index 6d4d9929320..00000000000 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsStatusbarItem.ts +++ /dev/null @@ -1,89 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { BreadcrumbsWidget, RenderedBreadcrumbsItem, BreadcrumbsItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { posix } from 'path'; -import URI from 'vs/base/common/uri'; -import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; - -export class BreadcrumbsStatusbarItem implements IStatusbarItem { - - private _widget: BreadcrumbsWidget; - private _disposables: IDisposable[] = []; - - constructor( - @IEditorService private readonly _editorService: IEditorService, - @IContextViewService private readonly _contextViewService: IContextViewService - ) { - } - - dispose(): void { - dispose(this._disposables); - } - - render(element: HTMLElement): IDisposable { - this._widget = new BreadcrumbsWidget(element); - this._widget.layout(300); - this._disposables.push(this._widget); - this._disposables.push(this._widget.onDidSelectItem(this._onDidSelectItem, this)); - this._disposables.push(this._editorService.onDidActiveEditorChange(this._onDidChangeActiveEditor, this)); - this._onDidChangeActiveEditor(); - return this; - } - - private _onDidChangeActiveEditor(): void { - let { activeEditor } = this._editorService; - if (!activeEditor) { - this._widget.replace(undefined, []); - return; - } - let resource = activeEditor.getResource(); - if (!resource) { - this._widget.replace(undefined, []); - return; - } - - interface Element { - name: string; - uri: URI; - } - - function render(element: Element, target: HTMLElement) { - target.innerText = getBaseLabel(element.uri); - target.title = getPathLabel(element.uri, undefined, undefined); - } - - let items: RenderedBreadcrumbsItem[] = []; - let path = resource.path; - while (path !== '/') { - let name = posix.basename(path); - let uri = resource.with({ path }); - path = posix.dirname(path); - items.unshift(new RenderedBreadcrumbsItem(render, { name, uri }, items.length !== 0)); - } - - this._widget.replace(undefined, items); - } - - private _onDidSelectItem(item: BreadcrumbsItem): void { - console.log(item, this._contextViewService._serviceBrand); - - // this._contextViewService.showContextView({ - // getAnchor() { - // return item.node; - // }, - // render(container) { - // container.innerText = JSON.stringify(item, undefined, 4); - // return null; - // } - // }); - } -} diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css index 2901388a828..7a30f6791f4 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css @@ -21,12 +21,15 @@ cursor: pointer; } +.monaco-breadcrumbs:focus .monaco-breadcrumb-item.active { + color: pink; +} + .monaco-breadcrumbs .monaco-breadcrumb-item:hover { color: var(--item-hover-color); background-color: var(--item-hover-background); } -.monaco-breadcrumbs .monaco-breadcrumb-item-more::after { - content: '〉'; - padding-left: 5px; +.monaco-breadcrumbs .monaco-breadcrumb-item-more { + border-right: 1px solid; } diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts index 3c23a030b22..e8ac7716b24 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts @@ -30,7 +30,7 @@ export class BreadcrumbsItem { export class SimpleBreadcrumbsItem extends BreadcrumbsItem { constructor(text: string, title: string = text, more: boolean = true) { - super(document.createElement('span'), more); + super(document.createElement('div'), more); this.node.innerText = text; this.node.title = title; } @@ -41,9 +41,9 @@ export class RenderedBreadcrumbsItem extends BreadcrumbsItem { private _disposables: IDisposable[] = []; - constructor(render: (element: E, container: HTMLSpanElement, bucket: IDisposable[]) => any, element: E, more: boolean) { - super(document.createElement('span'), more); - render(element, this.node, this._disposables); + constructor(render: (element: E, container: HTMLDivElement, bucket: IDisposable[]) => any, element: E, more: boolean) { + super(document.createElement('div'), more); + render(element, this.node as HTMLDivElement, this._disposables); } dispose() { @@ -55,7 +55,7 @@ export class RenderedBreadcrumbsItem extends BreadcrumbsItem { export class BreadcrumbsWidget { private readonly _disposables = new Array(); - private readonly _domNode: HTMLSpanElement; + private readonly _domNode: HTMLDivElement; private readonly _scrollable: DomScrollableElement; private _cachedWidth: number; @@ -63,15 +63,16 @@ export class BreadcrumbsWidget { readonly onDidSelectItem: Event = this._onDidSelectItem.event; private readonly _items = new Array(); - private readonly _nodes = new Array(); - private readonly _freeNodes = new Array(); - private _activeItem: number; + private readonly _nodes = new Array(); + private readonly _freeNodes = new Array(); + private _activeItem: number = -1; constructor( container: HTMLElement ) { - this._domNode = document.createElement('span'); + this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs'; + this._domNode.tabIndex = -1; this._scrollable = new DomScrollableElement(this._domNode, { vertical: ScrollbarVisibility.Hidden, horizontal: ScrollbarVisibility.Auto, @@ -103,16 +104,19 @@ export class BreadcrumbsWidget { this._domNode.focus(); } - select(nth: number): void { - if (typeof this._activeItem === 'number') { + select(nth: number): boolean { + if (this._activeItem !== -1) { dom.removeClass(this._nodes[this._activeItem], 'active'); + this._activeItem = -1; } - if (nth >= this._nodes.length) { - this._activeItem = nth; - let node = this._nodes[this._activeItem]; - dom.addClass(node, 'active'); - this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); + if (nth < 0 || nth >= this._nodes.length) { + return false; } + this._activeItem = nth; + let node = this._nodes[this._activeItem]; + dom.addClass(node, 'active'); + this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); + return true; } append(item: BreadcrumbsItem): void { @@ -143,7 +147,7 @@ export class BreadcrumbsWidget { // case b: more items -> render them for (; start < this._items.length; start++) { let item = this._items[start]; - let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('span'); + let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div'); this._renderItem(item, node); this._domNode.appendChild(node); this._nodes[start] = node; @@ -152,7 +156,7 @@ export class BreadcrumbsWidget { this.select(this._nodes.length - 1); } - private _renderItem(item: BreadcrumbsItem, container: HTMLSpanElement): void { + private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void { dom.clearNode(container); dom.append(container, item.node); dom.addClass(container, 'monaco-breadcrumb-item'); @@ -163,6 +167,7 @@ export class BreadcrumbsWidget { for (let el = event.target; el; el = el.parentElement) { let idx = this._nodes.indexOf(el as any); if (idx >= 0) { + this.select(idx); this._onDidSelectItem.fire(this._items[idx]); break; } diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts new file mode 100644 index 00000000000..198261cae8c --- /dev/null +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + + +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { posix } from 'path'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileKind } from 'vs/platform/files/common/files'; +import { FileLabel } from 'vs/workbench/browser/labels'; + +class Widget implements IOverlayWidget { + + breadcrumb: BreadcrumbsWidget; + ready: boolean; + getId(): string { + return 'EditorBreadcrumbs.Widget'; + } + + getDomNode(): HTMLElement { + let container = document.createElement('div'); + container.style.backgroundColor = 'white'; + let widget = new BreadcrumbsWidget(container); + this.breadcrumb = widget; + this.ready = true; + return container; + } + + getPosition(): IOverlayWidgetPosition { + return { + preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER + }; + } +} + +class BreadcrumbsUpdateEvent { + + private readonly _disposables = new Array(); + private readonly _emitter = new Emitter(); + + readonly event: Event = this._emitter.event; + + constructor(private readonly _editor: ICodeEditor) { + this._disposables.push(this._editor.onDidChangeModel(_ => this._emitter.fire())); + } +} + +export class EditorBreadcrumbs implements IEditorContribution { + + static get(editor: ICodeEditor): EditorBreadcrumbs { + return editor.getContribution('EditorBreadcrumbs'); + } + + private readonly _disposables = new Array(); + private readonly _widget: Widget; + private readonly _update: BreadcrumbsUpdateEvent; + + constructor( + private readonly _editor: ICodeEditor, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { + this._widget = new Widget(); + this._update = new BreadcrumbsUpdateEvent(this._editor); + this._editor.addOverlayWidget(this._widget); + this._update.event(this._onUpdate, this, this._disposables); + } + + getId(): string { + return 'EditorBreadcrumbs'; + } + + dispose(): void { + this._editor.removeOverlayWidget(this._widget); + } + + private _onUpdate(): void { + if (!this._widget.ready || !this._editor.getModel()) { + return; + } + let { uri } = this._editor.getModel(); + + interface Element { + name: string; + uri: URI; + kind: FileKind; + } + + const render = (element: Element, target: HTMLElement, disposables: IDisposable[]) => { + let label = this._instantiationService.createInstance(FileLabel, target, {}); + label.setFile(element.uri, { fileKind: element.kind, hidePath: true }); + disposables.push(label); + }; + + let items: RenderedBreadcrumbsItem[] = []; + let path = uri.path; + while (path !== '/') { + let first = items.length === 0; + let name = posix.basename(path); + uri = uri.with({ path }); + path = posix.dirname(path); + items.unshift(new RenderedBreadcrumbsItem( + render, + { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, + !first + )); + } + + this._widget.breadcrumb.replace(undefined, items); + } + + focus(): void { + this._widget.breadcrumb.focus(); + } + + // saveViewState?() { + // throw new Error('Method not implemented.'); + // } + // restoreViewState?(state: any): void { + // throw new Error('Method not implemented.'); + // } +} From ecaae775e43aa1504c2c1805d2d1463011e58fdf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 11:51:14 +0200 Subject: [PATCH 005/151] add focus/next/prev/back commands --- .../breadcrumbs.contribution.ts | 48 +++++++++++++++- .../electron-browser/breadcrumbsWidget.ts | 43 +++++++++++--- .../electron-browser/editorBreadcrumbs.ts | 57 +++++++++++++++---- 3 files changed, 128 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts index b0dc0ff39bb..8757b516def 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts @@ -10,6 +10,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { EditorBreadcrumbs } from 'vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; registerEditorContribution(EditorBreadcrumbs); @@ -23,6 +24,51 @@ registerEditorCommand(new BreadcrumbCommandCtor({ kbOpts: { weight: KeybindingsRegistry.WEIGHT.editorContrib(50), kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyCode.US_DOT + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT + } +})); + +registerEditorCommand(new BreadcrumbCommandCtor({ + id: 'breadcrumbs.focusNext', + precondition: undefined, + handler: x => x.focusNext(), + kbOpts: { + weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), + primary: KeyCode.RightArrow + } +})); + +registerEditorCommand(new BreadcrumbCommandCtor({ + id: 'breadcrumbs.focusPrev', + precondition: undefined, + handler: x => x.focusPrev(), + kbOpts: { + weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), + primary: KeyCode.LeftArrow + } +})); + +registerEditorCommand(new BreadcrumbCommandCtor({ + id: 'breadcrumbs.select', + precondition: undefined, + handler: x => x.select(), + kbOpts: { + weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), + primary: KeyCode.Enter, + secondary: [KeyCode.UpArrow, KeyCode.Space] + } +})); + +registerEditorCommand(new BreadcrumbCommandCtor({ + id: 'breadcrumbs.focusEditor', + precondition: undefined, + handler: x => x.editor.focus(), + kbOpts: { + weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), + primary: KeyCode.Escape } })); diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts index e8ac7716b24..f11495d93a6 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts @@ -60,12 +60,15 @@ export class BreadcrumbsWidget { private _cachedWidth: number; private readonly _onDidSelectItem = new Emitter(); + private readonly _onDidChangeFocus = new Emitter(); + readonly onDidSelectItem: Event = this._onDidSelectItem.event; + readonly onDidChangeFocus: Event = this._onDidChangeFocus.event; private readonly _items = new Array(); private readonly _nodes = new Array(); private readonly _freeNodes = new Array(); - private _activeItem: number = -1; + private _focusedItemIdx: number = -1; constructor( container: HTMLElement @@ -82,6 +85,11 @@ export class BreadcrumbsWidget { this._disposables.push(this._scrollable); this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); container.appendChild(this._scrollable.getDomNode()); + + let focusTracker = dom.trackFocus(this._domNode); + this._disposables.push(focusTracker); + this._disposables.push(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false))); + this._disposables.push(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true))); } dispose(): void { @@ -104,21 +112,38 @@ export class BreadcrumbsWidget { this._domNode.focus(); } - select(nth: number): boolean { - if (this._activeItem !== -1) { - dom.removeClass(this._nodes[this._activeItem], 'active'); - this._activeItem = -1; + focusPrev(): any { + this._focus((this._focusedItemIdx - 1 + this._nodes.length) % this._nodes.length); + this._domNode.focus(); + } + + focusNext(): any { + this._focus((this._focusedItemIdx + 1) % this._nodes.length); + this._domNode.focus(); + } + + private _focus(nth: number): boolean { + if (this._focusedItemIdx !== -1) { + dom.removeClass(this._nodes[this._focusedItemIdx], 'active'); + this._focusedItemIdx = -1; } if (nth < 0 || nth >= this._nodes.length) { return false; } - this._activeItem = nth; - let node = this._nodes[this._activeItem]; + this._focusedItemIdx = nth; + let node = this._nodes[this._focusedItemIdx]; dom.addClass(node, 'active'); this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); return true; } + select(): void { + if (this._focusedItemIdx !== -1) { + let item = this._items[this._focusedItemIdx]; + this._onDidSelectItem.fire(item); + } + } + append(item: BreadcrumbsItem): void { this._items.push(item); this._render(this._items.length - 1); @@ -153,7 +178,7 @@ export class BreadcrumbsWidget { this._nodes[start] = node; } this.layout(); - this.select(this._nodes.length - 1); + this._focus(this._nodes.length - 1); } private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void { @@ -167,7 +192,7 @@ export class BreadcrumbsWidget { for (let el = event.target; el; el = el.parentElement) { let idx = this._nodes.indexOf(el as any); if (idx >= 0) { - this.select(idx); + this._focus(idx); this._onDidSelectItem.fire(this._items[idx]); break; } diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts index 198261cae8c..970656cab15 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts @@ -8,23 +8,30 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; import { posix } from 'path'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { FileKind } from 'vs/platform/files/common/files'; import { FileLabel } from 'vs/workbench/browser/labels'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; class Widget implements IOverlayWidget { breadcrumb: BreadcrumbsWidget; ready: boolean; + + constructor(private _onFirstRender: Function) { + + } + getId(): string { return 'EditorBreadcrumbs.Widget'; } getDomNode(): HTMLElement { + setTimeout(() => this._onFirstRender()); let container = document.createElement('div'); container.style.backgroundColor = 'white'; let widget = new BreadcrumbsWidget(container); @@ -52,8 +59,11 @@ class BreadcrumbsUpdateEvent { } } + export class EditorBreadcrumbs implements IEditorContribution { + static CK_Focused = new RawContextKey('breadcrumbFocused', false); + static get(editor: ICodeEditor): EditorBreadcrumbs { return editor.getContribution('EditorBreadcrumbs'); } @@ -62,14 +72,16 @@ export class EditorBreadcrumbs implements IEditorContribution { private readonly _widget: Widget; private readonly _update: BreadcrumbsUpdateEvent; + private _ckFocused: IContextKey; + constructor( - private readonly _editor: ICodeEditor, - @IInstantiationService private readonly _instantiationService: IInstantiationService + readonly editor: ICodeEditor, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, ) { - this._widget = new Widget(); - this._update = new BreadcrumbsUpdateEvent(this._editor); - this._editor.addOverlayWidget(this._widget); - this._update.event(this._onUpdate, this, this._disposables); + this._widget = new Widget(() => this._onWidgetReady()); + this._update = new BreadcrumbsUpdateEvent(this.editor); + this.editor.addOverlayWidget(this._widget); } getId(): string { @@ -77,14 +89,23 @@ export class EditorBreadcrumbs implements IEditorContribution { } dispose(): void { - this._editor.removeOverlayWidget(this._widget); + dispose(this._disposables); + this.editor.removeOverlayWidget(this._widget); + this._ckFocused.reset(); + } + + private _onWidgetReady(): void { + this._ckFocused = EditorBreadcrumbs.CK_Focused.bindTo(this._contextKeyService); + this._disposables.push(this._widget.breadcrumb.onDidChangeFocus(value => this._ckFocused.set(value))); + this._disposables.push(this._widget.breadcrumb.onDidSelectItem(this._onDidSelectItem, this)); + this._update.event(this._onUpdate, this, this._disposables); } private _onUpdate(): void { - if (!this._widget.ready || !this._editor.getModel()) { + if (!this._widget.ready || !this.editor.getModel()) { return; } - let { uri } = this._editor.getModel(); + let { uri } = this.editor.getModel(); interface Element { name: string; @@ -115,10 +136,26 @@ export class EditorBreadcrumbs implements IEditorContribution { this._widget.breadcrumb.replace(undefined, items); } + private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { + console.log(item); + } + focus(): void { this._widget.breadcrumb.focus(); } + focusNext(): void { + this._widget.breadcrumb.focusNext(); + } + + focusPrev(): void { + this._widget.breadcrumb.focusPrev(); + } + + select(): void { + this._widget.breadcrumb.select(); + } + // saveViewState?() { // throw new Error('Method not implemented.'); // } From e35f72e5e9e55879331de69f55e4e1b2f5a27784 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 16:31:26 +0200 Subject: [PATCH 006/151] move breadcrumbs into editor group view --- .../ui/breadcrumbs}/breadcrumbsWidget.css | 12 +- .../ui/breadcrumbs}/breadcrumbsWidget.ts | 20 +- .../workbench/browser/parts/editor/editor.ts | 3 +- .../browser/parts/editor/editorBreadcrumbs.ts | 352 ++++++++++++++++++ .../browser/parts/editor/editorGroupView.ts | 42 ++- .../parts/editor/media/editorbreadcrumbs.css | 12 + .../electron-browser/breadcrumbsPicker.ts | 144 +++++++ .../electron-browser/editorBreadcrumbs.ts | 49 ++- .../group/common/editorGroupsService.ts | 14 +- src/vs/workbench/workbench.main.ts | 2 - 10 files changed, 610 insertions(+), 40 deletions(-) rename src/vs/{workbench/parts/breadcrumbs/electron-browser => base/browser/ui/breadcrumbs}/breadcrumbsWidget.css (72%) rename src/vs/{workbench/parts/breadcrumbs/electron-browser => base/browser/ui/breadcrumbs}/breadcrumbsWidget.ts (94%) create mode 100644 src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts create mode 100644 src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css create mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css similarity index 72% rename from src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css rename to src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 7a30f6791f4..f1556526d68 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -9,8 +9,6 @@ flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; - --item-hover-background: green; - --item-hover-color: inhert; } .monaco-breadcrumbs .monaco-breadcrumb-item { @@ -19,15 +17,7 @@ flex: 0 1 auto; white-space: nowrap; cursor: pointer; -} - -.monaco-breadcrumbs:focus .monaco-breadcrumb-item.active { - color: pink; -} - -.monaco-breadcrumbs .monaco-breadcrumb-item:hover { - color: var(--item-hover-color); - background-color: var(--item-hover-background); + align-self: center; } .monaco-breadcrumbs .monaco-breadcrumb-item-more { diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts similarity index 94% rename from src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts rename to src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index f11495d93a6..e83364656e6 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -38,11 +38,12 @@ export class SimpleBreadcrumbsItem extends BreadcrumbsItem { export class RenderedBreadcrumbsItem extends BreadcrumbsItem { - + readonly element: E; private _disposables: IDisposable[] = []; constructor(render: (element: E, container: HTMLDivElement, bucket: IDisposable[]) => any, element: E, more: boolean) { super(document.createElement('div'), more); + this.element = element; render(element, this.node as HTMLDivElement, this._disposables); } @@ -57,7 +58,6 @@ export class BreadcrumbsWidget { private readonly _disposables = new Array(); private readonly _domNode: HTMLDivElement; private readonly _scrollable: DomScrollableElement; - private _cachedWidth: number; private readonly _onDidSelectItem = new Emitter(); private readonly _onDidChangeFocus = new Emitter(); @@ -100,10 +100,12 @@ export class BreadcrumbsWidget { this._freeNodes.length = 0; } - layout(width: number = this._cachedWidth): void { - if (typeof width === 'number') { - this._cachedWidth = width; - this._domNode.style.width = `${this._cachedWidth}px`; + layout(dim: dom.Dimension): void { + if (!dim) { + this._scrollable.scanDomNode(); + } else { + this._domNode.style.width = `${dim.width}px`; + this._domNode.style.height = `${dim.height}px`; this._scrollable.scanDomNode(); } } @@ -124,7 +126,7 @@ export class BreadcrumbsWidget { private _focus(nth: number): boolean { if (this._focusedItemIdx !== -1) { - dom.removeClass(this._nodes[this._focusedItemIdx], 'active'); + dom.removeClass(this._nodes[this._focusedItemIdx], 'focused'); this._focusedItemIdx = -1; } if (nth < 0 || nth >= this._nodes.length) { @@ -132,7 +134,7 @@ export class BreadcrumbsWidget { } this._focusedItemIdx = nth; let node = this._nodes[this._focusedItemIdx]; - dom.addClass(node, 'active'); + dom.addClass(node, 'focused'); this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); return true; } @@ -177,7 +179,7 @@ export class BreadcrumbsWidget { this._domNode.appendChild(node); this._nodes[start] = node; } - this.layout(); + this.layout(undefined); this._focus(this._nodes.length - 1); } diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 74518dd76c1..f9b0c858a38 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -20,6 +20,7 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export const EDITOR_TITLE_HEIGHT = 35; +export const BREAD_CRUMPS_HEIGHT = 30; export const EDITOR_MIN_DIMENSIONS = new Dimension(220, 70); export const EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY); @@ -159,4 +160,4 @@ export interface EditorGroupsServiceImpl extends IEditorGroupsService { * A promise that resolves when groups have been restored. */ readonly whenRestored: TPromise; -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts new file mode 100644 index 00000000000..72f5ee5c617 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -0,0 +1,352 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./media/editorbreadcrumbs'; +import * as dom from 'vs/base/browser/dom'; +import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import * as paths from 'vs/base/common/paths'; +import URI from 'vs/base/common/uri'; +import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { FileLabel } from 'vs/workbench/browser/labels'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent } from 'vs/base/parts/tree/browser/tree'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { WorkbenchTree } from 'vs/platform/list/browser/listService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { isEqual } from 'vs/base/common/resources'; + +interface FileElement { + name: string; + uri: URI; + kind: FileKind; +} + +export class EditorBreadcrumbs implements IEditorBreadcrumbs { + + static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); + static CK_BreadcrumbsFocused = new RawContextKey('breadcrumbsFocused', false); + + private readonly _disposables = new Array(); + private readonly _domNode: HTMLDivElement; + private readonly _widget: BreadcrumbsWidget; + + private readonly _ckBreadcrumbsVisible: IContextKey; + private readonly _ckBreadcrumbsFocused: IContextKey; + + constructor( + container: HTMLElement, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IFileService private readonly _fileService: IFileService, + @IContextViewService private readonly _contextViewService: IContextViewService, + @IEditorService private readonly _editorService: IEditorService, + @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + this._domNode = document.createElement('div'); + this._domNode.className = 'editor-breadcrumbs'; + this._widget = new BreadcrumbsWidget(this._domNode); + this._widget.onDidSelectItem(this._onDidSelectItem, this, this._disposables); + this._widget.onDidChangeFocus(val => this._ckBreadcrumbsFocused.set(val), undefined, this._disposables); + container.appendChild(this._domNode); + + this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); + this._ckBreadcrumbsFocused = EditorBreadcrumbs.CK_BreadcrumbsFocused.bindTo(this._contextKeyService); + } + + dispose(): void { + dispose(this._disposables); + this._widget.dispose(); + this._ckBreadcrumbsVisible.reset(); + } + + layout(dim: dom.Dimension): void { + this._domNode.style.width = `${dim.width}px`; + this._domNode.style.height = `${dim.height}px`; + this._widget.layout(dim); + } + + setActive(value: boolean): void { + dom.toggleClass(this._domNode, 'active', value); + } + + openEditor(input: EditorInput): void { + + let uri = input.getResource(); + if (!uri || !this._fileService.canHandleResource(uri)) { + return this.closeEditor(undefined); + } + + this._ckBreadcrumbsVisible.set(true); + dom.toggleClass(this._domNode, 'hidden', false); + + + const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { + let label = this._instantiationService.createInstance(FileLabel, target, {}); + label.setFile(element.uri, { fileKind: element.kind, hidePath: true }); + disposables.push(label); + }; + + let items: RenderedBreadcrumbsItem[] = []; + let workspace = this._workspaceService.getWorkspaceFolder(uri); + let path = uri.path; + + while (true) { + if (workspace && isEqual(workspace.uri, uri, true) || path === '/') { + break; + } + + let first = items.length === 0; + let name = paths.basename(path); + uri = uri.with({ path }); + path = paths.dirname(path); + items.unshift(new RenderedBreadcrumbsItem( + render, + { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, + !first + )); + } + + this._widget.replace(undefined, items); + } + + closeEditor(input: EditorInput): void { + this._ckBreadcrumbsVisible.set(false); + dom.toggleClass(this._domNode, 'hidden', true); + } + + focus(): void { + this._widget.focus(); + } + + focusNext(): void { + this._widget.focusNext(); + } + + focusPrev(): void { + this._widget.focusPrev(); + } + + select(): void { + this._widget.select(); + } + + private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { + this._contextViewService.showContextView({ + getAnchor() { + return item.node; + }, + render: (container: HTMLElement) => { + let res = this._instantiationService.createInstance(BreadcrumbsFilePicker, container); + res.layout({ width: 250, height: 300 }); + res.setInput(item.element.uri.with({ path: paths.dirname(item.element.uri.path) })); + res.onDidPickElement(data => { + this._contextViewService.hideContextView(); + if (!data) { + return; + } + if (URI.isUri(data)) { + this._editorService.openEditor({ resource: data }); + } + }); + return res; + }, + }); + } +} + +export abstract class BreadcrumbsPicker { + + readonly focus: dom.IFocusTracker; + + protected readonly _onDidPickElement = new Emitter(); + readonly onDidPickElement: Event = this._onDidPickElement.event; + + protected readonly _disposables = new Array(); + protected readonly _domNode: HTMLDivElement; + protected readonly _tree: WorkbenchTree; + + constructor( + container: HTMLElement, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @IThemeService protected readonly _themeService: IThemeService, + ) { + this._domNode = document.createElement('div'); + // this._domNode.style.background = this._themeService.getTheme().getColor(colors.progressBarBackground).toString(); + container.appendChild(this._domNode); + + this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {}); + debounceEvent(this._tree.onDidChangeSelection, (last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables); + + this.focus = dom.trackFocus(this._domNode); + this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); + } + + dispose(): void { + dispose(this._disposables); + this._onDidPickElement.dispose(); + this._tree.dispose(); + this.focus.dispose(); + } + + layout(dim: dom.Dimension) { + this._domNode.style.width = `${dim.width}px`; + this._domNode.style.height = `${dim.height}px`; + this._tree.layout(dim.height, dim.width); + } + + protected abstract _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration; + protected abstract _onDidChangeSelection(e: any): void; +} + +export class FileDataSource implements IDataSource { + + private readonly _parents = new WeakMap(); + + constructor( + @IFileService private readonly _fileService: IFileService, + ) { } + + getId(tree: ITree, element: IFileStat | URI): string { + return URI.isUri(element) ? element.toString() : element.resource.toString(); + } + + hasChildren(tree: ITree, element: IFileStat | URI): boolean { + return URI.isUri(element) || element.isDirectory; + } + + getChildren(tree: ITree, element: IFileStat | URI): TPromise { + return this._fileService.resolveFile( + URI.isUri(element) ? element : element.resource + ).then(stat => { + for (const child of stat.children) { + this._parents.set(child, stat); + } + return stat.children; + }); + } + + getParent(tree: ITree, element: IFileStat | URI): TPromise { + return TPromise.as(URI.isUri(element) ? undefined : this._parents.get(element)); + } +} + +export class FileRenderer implements IRenderer { + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { } + + getHeight(tree: ITree, element: any): number { + return 22; + } + + getTemplateId(tree: ITree, element: any): string { + return 'FileStat'; + } + + renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { + return this._instantiationService.createInstance(FileLabel, container, {}); + } + + renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void { + templateData.setFile(element.resource, { hidePath: true, fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE }); + } + + disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { + templateData.dispose(); + } +} + +export class BreadcrumbsFilePicker extends BreadcrumbsPicker { + + + protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { + config.dataSource = this._instantiationService.createInstance(FileDataSource); + config.renderer = this._instantiationService.createInstance(FileRenderer); + return config; + } + + setInput(resource: URI): void { + this._tree.domFocus(); + this._tree.setInput(resource); + } + + protected _onDidChangeSelection(e: ISelectionEvent): void { + let [first] = e.selection; + let stat = first as IFileStat; + if (stat && !stat.isDirectory) { + this._onDidPickElement.fire(stat.resource); + } + } +} + + +//#region commands + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.focus', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT, + when: EditorBreadcrumbs.CK_BreadcrumbsVisible, + handler(accessor) { + let groups = accessor.get(IEditorGroupsService); + groups.activeGroup.breadcrumbs.focus(); + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.focusNext', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + primary: KeyCode.RightArrow, + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + handler(accessor) { + let groups = accessor.get(IEditorGroupsService); + groups.activeGroup.breadcrumbs.focusNext(); + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.focusPrevious', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + primary: KeyCode.LeftArrow, + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + handler(accessor) { + let groups = accessor.get(IEditorGroupsService); + groups.activeGroup.breadcrumbs.focusPrev(); + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.selectFocused', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + primary: KeyCode.Enter, + secondary: [KeyCode.UpArrow, KeyCode.Space], + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + handler(accessor) { + let groups = accessor.get(IEditorGroupsService); + groups.activeGroup.breadcrumbs.select(); + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.selectEditor', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + primary: KeyCode.Escape, + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + handler(accessor) { + let groups = accessor.get(IEditorGroupsService); + groups.activeGroup.activeControl.focus(); + } +}); + +//#endregion diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 0e9f49bd151..97615f52698 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -19,7 +19,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme'; -import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService'; import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl'; import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl'; import { IProgressService } from 'vs/platform/progress/common/progress'; @@ -34,7 +34,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceWorker } from 'vs/base/common/async'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; -import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, EDITOR_MIN_DIMENSIONS, EDITOR_MAX_DIMENSIONS, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, EDITOR_MIN_DIMENSIONS, EDITOR_MAX_DIMENSIONS, getActiveTextEditorOptions, IEditorOpeningEvent, BREAD_CRUMPS_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { join } from 'vs/base/common/paths'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -46,6 +46,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { EditorBreadcrumbs } from 'vs/workbench/browser/parts/editor/editorBreadcrumbs'; export class EditorGroupView extends Themable implements IEditorGroupView { @@ -104,6 +105,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private titleContainer: HTMLElement; private titleAreaControl: TitleControl; + private breadcrumbsContainer: HTMLElement; + private breadcrumbsControl: EditorBreadcrumbs; + private progressBar: ProgressBar; private editorContainer: HTMLElement; @@ -190,6 +194,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Title control this.createTitleAreaControl(); + // Breadcrumbs container + this.breadcrumbsContainer = document.createElement('div'); + addClass(this.breadcrumbsContainer, 'editor-breadcrumbs'); + this.element.appendChild(this.breadcrumbsContainer); + + // Breadcrumbs control + this.createEditorBreadcrumbs(); + // Editor container this.editorContainer = document.createElement('div'); addClass(this.editorContainer, 'editor-container'); @@ -396,6 +408,16 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } + private createEditorBreadcrumbs(): void { + + if (this.breadcrumbsControl) { + this.breadcrumbsControl.dispose(); + clearNode(this.breadcrumbsContainer); + } + + this.breadcrumbsControl = this.scopedInstantiationService.createInstance(EditorBreadcrumbs, this.breadcrumbsContainer); + } + private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): TPromise { if (this._group.count === 0) { return TPromise.as(void 0); // nothing to show @@ -592,6 +614,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this._label; } + get breadcrumbs(): IEditorBreadcrumbs { + return this.breadcrumbsControl; + } + get disposed(): boolean { return this._disposed; } @@ -617,6 +643,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update title control this.titleAreaControl.setActive(isActive); + this.breadcrumbsControl.setActive(isActive); + // Update styles this.updateStyles(); @@ -788,6 +816,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Show in title control after editor control because some actions depend on it this.titleAreaControl.openEditor(editor); + this.breadcrumbsControl.openEditor(editor); + return openEditorPromise; } @@ -955,8 +985,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.doCloseInactiveEditor(editor); } - // Forward to title control + // Forward to title control & breadcrumbs this.titleAreaControl.closeEditor(editor); + this.breadcrumbsControl.closeEditor(editor); } private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void { @@ -1342,7 +1373,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to controls this.titleAreaControl.layout(new Dimension(this.dimension.width, EDITOR_TITLE_HEIGHT)); - this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - EDITOR_TITLE_HEIGHT)); + this.breadcrumbsControl.layout(new Dimension(this.dimension.width, BREAD_CRUMPS_HEIGHT)); + this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + BREAD_CRUMPS_HEIGHT))); } toJSON(): ISerializedEditorGroup { @@ -1362,6 +1394,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.titleAreaControl.dispose(); + this.breadcrumbsControl.dispose(); + super.dispose(); } } diff --git a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css new file mode 100644 index 00000000000..36815a8d225 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench>.part.editor>.content .editor-breadcrumbs .monaco-breadcrumbs { + --color-item-focused: pink; +} + +.monaco-workbench>.part.editor>.content .editor-breadcrumbs .monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { + color: var(--color-item-focused); +} diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts new file mode 100644 index 00000000000..4deeeb91658 --- /dev/null +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Dimension, trackFocus, IFocusTracker } from 'vs/base/browser/dom'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent } from 'vs/base/parts/tree/browser/tree'; +import { WorkbenchTree } from 'vs/platform/list/browser/listService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IFileService, IFileStat, FileKind } from 'vs/platform/files/common/files'; +import { FileLabel } from 'vs/workbench/browser/labels'; +import URI from 'vs/base/common/uri'; +import { Emitter, Event, debounceEvent } from 'vs/base/common/event'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export abstract class BreadcrumbsPicker { + + readonly focus: IFocusTracker; + + protected readonly _onDidPickElement = new Emitter(); + readonly onDidPickElement: Event = this._onDidPickElement.event; + + protected readonly _disposables = new Array(); + protected readonly _domNode: HTMLDivElement; + protected readonly _tree: WorkbenchTree; + + constructor( + container: HTMLElement, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @IThemeService protected readonly _themeService: IThemeService, + ) { + this._domNode = document.createElement('div'); + // this._domNode.style.background = this._themeService.getTheme().getColor(colors.progressBarBackground).toString(); + container.appendChild(this._domNode); + + this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {}); + debounceEvent(this._tree.onDidChangeSelection, (last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables); + + this.focus = trackFocus(this._domNode); + this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); + } + + dispose(): void { + dispose(this._disposables); + this._onDidPickElement.dispose(); + this._tree.dispose(); + this.focus.dispose(); + } + + layout(dim: Dimension) { + this._domNode.style.width = `${dim.width}px`; + this._domNode.style.height = `${dim.height}px`; + this._tree.layout(dim.height, dim.width); + } + + protected abstract _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration; + protected abstract _onDidChangeSelection(e: any): void; +} + +export class FileDataSource implements IDataSource { + + private readonly _parents = new WeakMap(); + + constructor( + @IFileService private readonly _fileService: IFileService, + ) { } + + getId(tree: ITree, element: IFileStat | URI): string { + return URI.isUri(element) ? element.toString() : element.resource.toString(); + } + + hasChildren(tree: ITree, element: IFileStat | URI): boolean { + return URI.isUri(element) || element.isDirectory; + } + + getChildren(tree: ITree, element: IFileStat | URI): TPromise { + return this._fileService.resolveFile( + URI.isUri(element) ? element : element.resource + ).then(stat => { + for (const child of stat.children) { + this._parents.set(child, stat); + } + return stat.children; + }); + } + + getParent(tree: ITree, element: IFileStat | URI): TPromise { + return TPromise.as(URI.isUri(element) ? undefined : this._parents.get(element)); + } +} + +export class FileRenderer implements IRenderer { + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { } + + getHeight(tree: ITree, element: any): number { + return 22; + } + + getTemplateId(tree: ITree, element: any): string { + return 'FileStat'; + } + + renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { + return this._instantiationService.createInstance(FileLabel, container, {}); + } + + renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void { + templateData.setFile(element.resource, { hidePath: true, fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE }); + } + + disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { + templateData.dispose(); + } +} + +export class BreadcrumbsFilePicker extends BreadcrumbsPicker { + + + protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { + config.dataSource = this._instantiationService.createInstance(FileDataSource); + config.renderer = this._instantiationService.createInstance(FileRenderer); + return config; + } + + setInput(resource: URI): void { + this._tree.domFocus(); + this._tree.setInput(resource); + } + + protected _onDidChangeSelection(e: ISelectionEvent): void { + let [first] = e.selection; + let stat = first as IFileStat; + if (stat && !stat.isDirectory) { + this._onDidPickElement.fire(stat.resource); + } + } +} diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts index 970656cab15..a45d920e4a9 100644 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts +++ b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts @@ -7,7 +7,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsWidget'; +import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; @@ -16,6 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { FileKind } from 'vs/platform/files/common/files'; import { FileLabel } from 'vs/workbench/browser/labels'; import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { BreadcrumbsFilePicker } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; class Widget implements IOverlayWidget { @@ -59,6 +62,11 @@ class BreadcrumbsUpdateEvent { } } +interface FileElement { + name: string; + uri: URI; + kind: FileKind; +} export class EditorBreadcrumbs implements IEditorContribution { @@ -78,6 +86,8 @@ export class EditorBreadcrumbs implements IEditorContribution { readonly editor: ICodeEditor, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IContextViewService private readonly _contextViewService: IContextViewService, + @IEditorService private readonly _editorService: IEditorService, ) { this._widget = new Widget(() => this._onWidgetReady()); this._update = new BreadcrumbsUpdateEvent(this.editor); @@ -99,6 +109,7 @@ export class EditorBreadcrumbs implements IEditorContribution { this._disposables.push(this._widget.breadcrumb.onDidChangeFocus(value => this._ckFocused.set(value))); this._disposables.push(this._widget.breadcrumb.onDidSelectItem(this._onDidSelectItem, this)); this._update.event(this._onUpdate, this, this._disposables); + this._onUpdate(); } private _onUpdate(): void { @@ -107,26 +118,20 @@ export class EditorBreadcrumbs implements IEditorContribution { } let { uri } = this.editor.getModel(); - interface Element { - name: string; - uri: URI; - kind: FileKind; - } - - const render = (element: Element, target: HTMLElement, disposables: IDisposable[]) => { + const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { let label = this._instantiationService.createInstance(FileLabel, target, {}); label.setFile(element.uri, { fileKind: element.kind, hidePath: true }); disposables.push(label); }; - let items: RenderedBreadcrumbsItem[] = []; + let items: RenderedBreadcrumbsItem[] = []; let path = uri.path; while (path !== '/') { let first = items.length === 0; let name = posix.basename(path); uri = uri.with({ path }); path = posix.dirname(path); - items.unshift(new RenderedBreadcrumbsItem( + items.unshift(new RenderedBreadcrumbsItem( render, { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, !first @@ -136,8 +141,28 @@ export class EditorBreadcrumbs implements IEditorContribution { this._widget.breadcrumb.replace(undefined, items); } - private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { - console.log(item); + private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { + + this._contextViewService.showContextView({ + getAnchor() { + return item.node; + }, + render: (container: HTMLElement) => { + let res = this._instantiationService.createInstance(BreadcrumbsFilePicker, container); + res.layout({ width: 300, height: 450 }); + res.setInput(item.element.uri.with({ path: posix.dirname(item.element.uri.path) })); + res.onDidPickElement(data => { + this._contextViewService.hideContextView(); + if (!data) { + return; + } + if (URI.isUri(data)) { + this._editorService.openEditor({ resource: data }); + } + }); + return res; + }, + }); } focus(): void { diff --git a/src/vs/workbench/services/group/common/editorGroupsService.ts b/src/vs/workbench/services/group/common/editorGroupsService.ts index 138849ffa8e..7df05d47a0a 100644 --- a/src/vs/workbench/services/group/common/editorGroupsService.ts +++ b/src/vs/workbench/services/group/common/editorGroupsService.ts @@ -312,6 +312,13 @@ export interface IGroupChangeEvent { editorIndex?: number; } +export interface IEditorBreadcrumbs { + focus(): void; + focusNext(): void; + focusPrev(): void; + select(): void; +} + export interface IEditorGroup { /** @@ -332,6 +339,11 @@ export interface IEditorGroup { */ readonly label: string; + /** + * + */ + readonly breadcrumbs: IEditorBreadcrumbs; + /** * The active control is the currently visible control of the group. */ @@ -476,4 +488,4 @@ export interface IEditorGroup { * Invoke a function in the context of the services of this group. */ invokeWithinContext(fn: (accessor: ServicesAccessor) => T): T; -} \ No newline at end of file +} diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 34cd6c0f235..4bb1eda080e 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -94,8 +94,6 @@ import 'vs/workbench/electron-browser/workbench'; import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; -import 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution'; - import 'vs/workbench/parts/tasks/electron-browser/task.contribution'; import 'vs/workbench/parts/emmet/browser/emmet.browser.contribution'; From 12a5327e67a5b0eec839e8142716b9dce47d271f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 16:32:30 +0200 Subject: [PATCH 007/151] remove parts/breadcrumbs --- .../breadcrumbs.contribution.ts | 74 ------- .../electron-browser/breadcrumbsPicker.ts | 144 ------------- .../electron-browser/editorBreadcrumbs.ts | 190 ------------------ 3 files changed, 408 deletions(-) delete mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts delete mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts delete mode 100644 src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts deleted file mode 100644 index 8757b516def..00000000000 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbs.contribution.ts +++ /dev/null @@ -1,74 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { EditorBreadcrumbs } from 'vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; - -registerEditorContribution(EditorBreadcrumbs); - - -const BreadcrumbCommandCtor = EditorCommand.bindToContribution(EditorBreadcrumbs.get); - -registerEditorCommand(new BreadcrumbCommandCtor({ - id: 'breadcrumbs.focus', - precondition: undefined, - handler: x => x.focus(), - kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), - kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT - } -})); - -registerEditorCommand(new BreadcrumbCommandCtor({ - id: 'breadcrumbs.focusNext', - precondition: undefined, - handler: x => x.focusNext(), - kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), - kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), - primary: KeyCode.RightArrow - } -})); - -registerEditorCommand(new BreadcrumbCommandCtor({ - id: 'breadcrumbs.focusPrev', - precondition: undefined, - handler: x => x.focusPrev(), - kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), - kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), - primary: KeyCode.LeftArrow - } -})); - -registerEditorCommand(new BreadcrumbCommandCtor({ - id: 'breadcrumbs.select', - precondition: undefined, - handler: x => x.select(), - kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), - kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), - primary: KeyCode.Enter, - secondary: [KeyCode.UpArrow, KeyCode.Space] - } -})); - -registerEditorCommand(new BreadcrumbCommandCtor({ - id: 'breadcrumbs.focusEditor', - precondition: undefined, - handler: x => x.editor.focus(), - kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), - kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, EditorBreadcrumbs.CK_Focused), - primary: KeyCode.Escape - } -})); diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts deleted file mode 100644 index 4deeeb91658..00000000000 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker.ts +++ /dev/null @@ -1,144 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { Dimension, trackFocus, IFocusTracker } from 'vs/base/browser/dom'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent } from 'vs/base/parts/tree/browser/tree'; -import { WorkbenchTree } from 'vs/platform/list/browser/listService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileService, IFileStat, FileKind } from 'vs/platform/files/common/files'; -import { FileLabel } from 'vs/workbench/browser/labels'; -import URI from 'vs/base/common/uri'; -import { Emitter, Event, debounceEvent } from 'vs/base/common/event'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; - -export abstract class BreadcrumbsPicker { - - readonly focus: IFocusTracker; - - protected readonly _onDidPickElement = new Emitter(); - readonly onDidPickElement: Event = this._onDidPickElement.event; - - protected readonly _disposables = new Array(); - protected readonly _domNode: HTMLDivElement; - protected readonly _tree: WorkbenchTree; - - constructor( - container: HTMLElement, - @IInstantiationService protected readonly _instantiationService: IInstantiationService, - @IThemeService protected readonly _themeService: IThemeService, - ) { - this._domNode = document.createElement('div'); - // this._domNode.style.background = this._themeService.getTheme().getColor(colors.progressBarBackground).toString(); - container.appendChild(this._domNode); - - this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {}); - debounceEvent(this._tree.onDidChangeSelection, (last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables); - - this.focus = trackFocus(this._domNode); - this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); - } - - dispose(): void { - dispose(this._disposables); - this._onDidPickElement.dispose(); - this._tree.dispose(); - this.focus.dispose(); - } - - layout(dim: Dimension) { - this._domNode.style.width = `${dim.width}px`; - this._domNode.style.height = `${dim.height}px`; - this._tree.layout(dim.height, dim.width); - } - - protected abstract _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration; - protected abstract _onDidChangeSelection(e: any): void; -} - -export class FileDataSource implements IDataSource { - - private readonly _parents = new WeakMap(); - - constructor( - @IFileService private readonly _fileService: IFileService, - ) { } - - getId(tree: ITree, element: IFileStat | URI): string { - return URI.isUri(element) ? element.toString() : element.resource.toString(); - } - - hasChildren(tree: ITree, element: IFileStat | URI): boolean { - return URI.isUri(element) || element.isDirectory; - } - - getChildren(tree: ITree, element: IFileStat | URI): TPromise { - return this._fileService.resolveFile( - URI.isUri(element) ? element : element.resource - ).then(stat => { - for (const child of stat.children) { - this._parents.set(child, stat); - } - return stat.children; - }); - } - - getParent(tree: ITree, element: IFileStat | URI): TPromise { - return TPromise.as(URI.isUri(element) ? undefined : this._parents.get(element)); - } -} - -export class FileRenderer implements IRenderer { - - constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, - ) { } - - getHeight(tree: ITree, element: any): number { - return 22; - } - - getTemplateId(tree: ITree, element: any): string { - return 'FileStat'; - } - - renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { - return this._instantiationService.createInstance(FileLabel, container, {}); - } - - renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void { - templateData.setFile(element.resource, { hidePath: true, fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE }); - } - - disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { - templateData.dispose(); - } -} - -export class BreadcrumbsFilePicker extends BreadcrumbsPicker { - - - protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { - config.dataSource = this._instantiationService.createInstance(FileDataSource); - config.renderer = this._instantiationService.createInstance(FileRenderer); - return config; - } - - setInput(resource: URI): void { - this._tree.domFocus(); - this._tree.setInput(resource); - } - - protected _onDidChangeSelection(e: ISelectionEvent): void { - let [first] = e.selection; - let stat = first as IFileStat; - if (stat && !stat.isDirectory) { - this._onDidPickElement.fire(stat.resource); - } - } -} diff --git a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts b/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts deleted file mode 100644 index a45d920e4a9..00000000000 --- a/src/vs/workbench/parts/breadcrumbs/electron-browser/editorBreadcrumbs.ts +++ /dev/null @@ -1,190 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - - -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import URI from 'vs/base/common/uri'; -import { posix } from 'path'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { FileKind } from 'vs/platform/files/common/files'; -import { FileLabel } from 'vs/workbench/browser/labels'; -import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { BreadcrumbsFilePicker } from 'vs/workbench/parts/breadcrumbs/electron-browser/breadcrumbsPicker'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; - -class Widget implements IOverlayWidget { - - breadcrumb: BreadcrumbsWidget; - ready: boolean; - - constructor(private _onFirstRender: Function) { - - } - - getId(): string { - return 'EditorBreadcrumbs.Widget'; - } - - getDomNode(): HTMLElement { - setTimeout(() => this._onFirstRender()); - let container = document.createElement('div'); - container.style.backgroundColor = 'white'; - let widget = new BreadcrumbsWidget(container); - this.breadcrumb = widget; - this.ready = true; - return container; - } - - getPosition(): IOverlayWidgetPosition { - return { - preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER - }; - } -} - -class BreadcrumbsUpdateEvent { - - private readonly _disposables = new Array(); - private readonly _emitter = new Emitter(); - - readonly event: Event = this._emitter.event; - - constructor(private readonly _editor: ICodeEditor) { - this._disposables.push(this._editor.onDidChangeModel(_ => this._emitter.fire())); - } -} - -interface FileElement { - name: string; - uri: URI; - kind: FileKind; -} - -export class EditorBreadcrumbs implements IEditorContribution { - - static CK_Focused = new RawContextKey('breadcrumbFocused', false); - - static get(editor: ICodeEditor): EditorBreadcrumbs { - return editor.getContribution('EditorBreadcrumbs'); - } - - private readonly _disposables = new Array(); - private readonly _widget: Widget; - private readonly _update: BreadcrumbsUpdateEvent; - - private _ckFocused: IContextKey; - - constructor( - readonly editor: ICodeEditor, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IContextViewService private readonly _contextViewService: IContextViewService, - @IEditorService private readonly _editorService: IEditorService, - ) { - this._widget = new Widget(() => this._onWidgetReady()); - this._update = new BreadcrumbsUpdateEvent(this.editor); - this.editor.addOverlayWidget(this._widget); - } - - getId(): string { - return 'EditorBreadcrumbs'; - } - - dispose(): void { - dispose(this._disposables); - this.editor.removeOverlayWidget(this._widget); - this._ckFocused.reset(); - } - - private _onWidgetReady(): void { - this._ckFocused = EditorBreadcrumbs.CK_Focused.bindTo(this._contextKeyService); - this._disposables.push(this._widget.breadcrumb.onDidChangeFocus(value => this._ckFocused.set(value))); - this._disposables.push(this._widget.breadcrumb.onDidSelectItem(this._onDidSelectItem, this)); - this._update.event(this._onUpdate, this, this._disposables); - this._onUpdate(); - } - - private _onUpdate(): void { - if (!this._widget.ready || !this.editor.getModel()) { - return; - } - let { uri } = this.editor.getModel(); - - const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { - let label = this._instantiationService.createInstance(FileLabel, target, {}); - label.setFile(element.uri, { fileKind: element.kind, hidePath: true }); - disposables.push(label); - }; - - let items: RenderedBreadcrumbsItem[] = []; - let path = uri.path; - while (path !== '/') { - let first = items.length === 0; - let name = posix.basename(path); - uri = uri.with({ path }); - path = posix.dirname(path); - items.unshift(new RenderedBreadcrumbsItem( - render, - { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, - !first - )); - } - - this._widget.breadcrumb.replace(undefined, items); - } - - private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { - - this._contextViewService.showContextView({ - getAnchor() { - return item.node; - }, - render: (container: HTMLElement) => { - let res = this._instantiationService.createInstance(BreadcrumbsFilePicker, container); - res.layout({ width: 300, height: 450 }); - res.setInput(item.element.uri.with({ path: posix.dirname(item.element.uri.path) })); - res.onDidPickElement(data => { - this._contextViewService.hideContextView(); - if (!data) { - return; - } - if (URI.isUri(data)) { - this._editorService.openEditor({ resource: data }); - } - }); - return res; - }, - }); - } - - focus(): void { - this._widget.breadcrumb.focus(); - } - - focusNext(): void { - this._widget.breadcrumb.focusNext(); - } - - focusPrev(): void { - this._widget.breadcrumb.focusPrev(); - } - - select(): void { - this._widget.breadcrumb.select(); - } - - // saveViewState?() { - // throw new Error('Method not implemented.'); - // } - // restoreViewState?(state: any): void { - // throw new Error('Method not implemented.'); - // } -} From 7b12b499055bcb078887cd5a1f71b10a94bf8869 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 16:33:36 +0200 Subject: [PATCH 008/151] fix compile --- src/vs/workbench/test/workbenchTestServices.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index ce5053d10fe..6c4ba8d0cb5 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -67,7 +67,7 @@ import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensi import { IKeybindingService } from '../../platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService'; import { IEditorService, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -577,6 +577,7 @@ export class TestEditorGroup implements IEditorGroupView { constructor(public id: number) { } group: EditorGroup = void 0; + breadcrumbs: IEditorBreadcrumbs; activeControl: IEditor; activeEditor: IEditorInput; previewEditor: IEditorInput; From a3660bf2d96bdd12a07ad1ac8700dea7f38995ec Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 18:09:18 +0200 Subject: [PATCH 009/151] enable icons --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 72f5ee5c617..687de6d3daa 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -57,7 +57,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { this._domNode = document.createElement('div'); - this._domNode.className = 'editor-breadcrumbs'; + dom.addClasses(this._domNode, 'editor-breadcrumbs', 'show-file-icons'); this._widget = new BreadcrumbsWidget(this._domNode); this._widget.onDidSelectItem(this._onDidSelectItem, this, this._disposables); this._widget.onDidChangeFocus(val => this._ckBreadcrumbsFocused.set(val), undefined, this._disposables); @@ -150,6 +150,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { return item.node; }, render: (container: HTMLElement) => { + dom.addClasses(container, 'show-file-icons'); let res = this._instantiationService.createInstance(BreadcrumbsFilePicker, container); res.layout({ width: 250, height: 300 }); res.setInput(item.element.uri.with({ path: paths.dirname(item.element.uri.path) })); From ba50059fcace300396ba3b44c5774ce462b308ce Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 18:09:28 +0200 Subject: [PATCH 010/151] remove separator --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index f1556526d68..51dd57c694c 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -19,7 +19,3 @@ cursor: pointer; align-self: center; } - -.monaco-breadcrumbs .monaco-breadcrumb-item-more { - border-right: 1px solid; -} From 801ac81ce6d5bc1d0e1dbd89475cec34d4d90024 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 18:13:08 +0200 Subject: [PATCH 011/151] fix focus issue --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index e83364656e6..ceb9c4c3438 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -125,7 +125,7 @@ export class BreadcrumbsWidget { } private _focus(nth: number): boolean { - if (this._focusedItemIdx !== -1) { + if (this._focusedItemIdx >= 0 && this._focusedItemIdx < this._nodes.length) { dom.removeClass(this._nodes[this._focusedItemIdx], 'focused'); this._focusedItemIdx = -1; } From f01aa6eb19450bd978d5aadb87d2661eb82157b0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 18:25:55 +0200 Subject: [PATCH 012/151] add picker brackground color --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 687de6d3daa..39f93bfd6c6 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -28,6 +28,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isEqual } from 'vs/base/common/resources'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; interface FileElement { name: string; @@ -186,7 +187,7 @@ export abstract class BreadcrumbsPicker { @IThemeService protected readonly _themeService: IThemeService, ) { this._domNode = document.createElement('div'); - // this._domNode.style.background = this._themeService.getTheme().getColor(colors.progressBarBackground).toString(); + this._domNode.style.background = this._themeService.getTheme().getColor(SIDE_BAR_BACKGROUND).toString(); container.appendChild(this._domNode); this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {}); From 6992f948e66fc87cd713b6d1c83b3cd0ebac73b1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Jun 2018 18:43:29 +0200 Subject: [PATCH 013/151] focus group when selecting breadcrumbs --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts | 8 +++++--- .../workbench/browser/parts/editor/editorBreadcrumbs.ts | 6 +++++- src/vs/workbench/browser/parts/editor/editorGroupView.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index ceb9c4c3438..bbb9aa1abf2 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -60,9 +60,11 @@ export class BreadcrumbsWidget { private readonly _scrollable: DomScrollableElement; private readonly _onDidSelectItem = new Emitter(); + private readonly _onDidFocusItem = new Emitter(); private readonly _onDidChangeFocus = new Emitter(); readonly onDidSelectItem: Event = this._onDidSelectItem.event; + readonly onDidFocusItem: Event = this._onDidFocusItem.event; readonly onDidChangeFocus: Event = this._onDidChangeFocus.event; private readonly _items = new Array(); @@ -133,9 +135,9 @@ export class BreadcrumbsWidget { return false; } this._focusedItemIdx = nth; - let node = this._nodes[this._focusedItemIdx]; - dom.addClass(node, 'focused'); - this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); + dom.addClass(this._nodes[this._focusedItemIdx], 'focused'); + this._scrollable.setScrollPosition({ scrollLeft: this._nodes[this._focusedItemIdx].offsetLeft }); + this._onDidFocusItem.fire(this._items[this._focusedItemIdx]); return true; } diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 39f93bfd6c6..f3d1bea4c7c 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -18,7 +18,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { FileLabel } from 'vs/workbench/browser/labels'; import { EditorInput } from 'vs/workbench/common/editor'; -import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IEditorBreadcrumbs, IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent } from 'vs/base/parts/tree/browser/tree'; import { TPromise } from 'vs/base/common/winjs.base'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; @@ -50,6 +50,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { constructor( container: HTMLElement, + private readonly _editorGroup: IEditorGroup, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IFileService private readonly _fileService: IFileService, @IContextViewService private readonly _contextViewService: IContextViewService, @@ -146,6 +147,9 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { + + this._editorGroup.focus(); + this._contextViewService.showContextView({ getAnchor() { return item.node; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 97615f52698..ce9f5dbf209 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -415,7 +415,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { clearNode(this.breadcrumbsContainer); } - this.breadcrumbsControl = this.scopedInstantiationService.createInstance(EditorBreadcrumbs, this.breadcrumbsContainer); + this.breadcrumbsControl = this.scopedInstantiationService.createInstance(EditorBreadcrumbs, this.breadcrumbsContainer, this); } private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): TPromise { From d4af61a17773d0433777a5dce42d53b688f4cc5b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 10:28:24 +0200 Subject: [PATCH 014/151] support styles in breadcrumb widget --- .../ui/breadcrumbs/breadcrumbsWidget.css | 10 ++- .../ui/breadcrumbs/breadcrumbsWidget.ts | 73 +++++++++++++++++-- src/vs/platform/theme/common/styler.ts | 32 +++++++- .../workbench/browser/parts/editor/editor.ts | 2 +- .../browser/parts/editor/editorBreadcrumbs.ts | 11 ++- .../browser/parts/editor/editorGroupView.ts | 6 +- .../parts/editor/media/editorbreadcrumbs.css | 8 +- 7 files changed, 122 insertions(+), 20 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 51dd57c694c..be772fca1bb 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -12,10 +12,16 @@ } .monaco-breadcrumbs .monaco-breadcrumb-item { - display: inline-block; - padding: 0 5px 0 5px; + display: flex; + align-items: center; + padding: 3px; flex: 0 1 auto; white-space: nowrap; cursor: pointer; align-self: center; + height: 100%; +} + +.monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ + padding-left: 8px; } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index bbb9aa1abf2..564a4cd67bf 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -12,6 +12,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Event, Emitter } from 'vs/base/common/event'; +import { Color } from 'vs/base/common/color'; export class BreadcrumbsItem { @@ -53,10 +54,23 @@ export class RenderedBreadcrumbsItem extends BreadcrumbsItem { } } +export interface IBreadcrumbsWidgetStyles { + breadcrumbsBackground?: Color; + breadcrumbsItemHoverBackground?: Color; + breadcrumbsItemHoverForeground?: Color; + breadcrumbsItemFocusBackground?: Color; + breadcrumbsItemFocusForeground?: Color; + breadcrumbsActiveItemSelectionBackground?: Color; + breadcrumbsActiveItemSelectionForeground?: Color; + breadcrumbsInactiveItemSelectionBackground?: Color; + breadcrumbsInactiveItemSelectionForeground?: Color; +} + export class BreadcrumbsWidget { private readonly _disposables = new Array(); private readonly _domNode: HTMLDivElement; + private readonly _styleElement: HTMLStyleElement; private readonly _scrollable: DomScrollableElement; private readonly _onDidSelectItem = new Emitter(); @@ -70,7 +84,9 @@ export class BreadcrumbsWidget { private readonly _items = new Array(); private readonly _nodes = new Array(); private readonly _freeNodes = new Array(); + private _focusedItemIdx: number = -1; + private _selectedItemIdx: number = -1; constructor( container: HTMLElement @@ -88,6 +104,8 @@ export class BreadcrumbsWidget { this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); container.appendChild(this._scrollable.getDomNode()); + this._styleElement = dom.createStyleSheet(this._domNode); + let focusTracker = dom.trackFocus(this._domNode); this._disposables.push(focusTracker); this._disposables.push(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false))); @@ -112,6 +130,37 @@ export class BreadcrumbsWidget { } } + style(style: IBreadcrumbsWidgetStyles): void { + let content = ''; + if (style.breadcrumbsItemFocusForeground) { + content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsItemFocusForeground}}\n`; + } + if (style.breadcrumbsItemFocusBackground) { + content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { background-color: ${style.breadcrumbsItemFocusBackground}}\n`; + } + if (style.breadcrumbsItemFocusForeground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover { color: ${style.breadcrumbsItemHoverForeground}}\n`; + } + if (style.breadcrumbsItemFocusBackground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover { background-color: ${style.breadcrumbsItemHoverBackground}}\n`; + } + if (style.breadcrumbsActiveItemSelectionForeground) { + content += `.monaco-breadcrumbs:hover .monaco-breadcrumb-item.selected { color: ${style.breadcrumbsActiveItemSelectionForeground}}\n`; + } + if (style.breadcrumbsActiveItemSelectionBackground) { + content += `.monaco-breadcrumbs:hover .monaco-breadcrumb-item.selected { background-color: ${style.breadcrumbsActiveItemSelectionBackground}}\n`; + } + if (style.breadcrumbsInactiveItemSelectionForeground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item.selected { color: ${style.breadcrumbsInactiveItemSelectionForeground}}\n`; + } + if (style.breadcrumbsInactiveItemSelectionBackground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item.selected { background-color: ${style.breadcrumbsInactiveItemSelectionBackground}}\n`; + } + if (this._styleElement.innerHTML !== content) { + this._styleElement.innerHTML = content; + } + } + focus(): void { this._domNode.focus(); } @@ -141,11 +190,25 @@ export class BreadcrumbsWidget { return true; } - select(): void { - if (this._focusedItemIdx !== -1) { - let item = this._items[this._focusedItemIdx]; - this._onDidSelectItem.fire(item); + getFocusedItem(): BreadcrumbsItem { + return this._items[this._focusedItemIdx]; + } + + select(item: BreadcrumbsItem): void { + this._select(this._items.indexOf(item)); + } + + private _select(nth: number): void { + if (this._selectedItemIdx >= 0 && this._selectedItemIdx < this._nodes.length) { + dom.removeClass(this._nodes[this._selectedItemIdx], 'selected'); + this._selectedItemIdx = -1; } + if (nth < 0 || nth >= this._nodes.length) { + return; + } + this._selectedItemIdx = nth; + dom.addClass(this._nodes[this._selectedItemIdx], 'selected'); + this._onDidSelectItem.fire(this._items[this._selectedItemIdx]); } append(item: BreadcrumbsItem): void { @@ -197,7 +260,7 @@ export class BreadcrumbsWidget { let idx = this._nodes.indexOf(el as any); if (idx >= 0) { this._focus(idx); - this._onDidSelectItem.fire(this._items[idx]); + this._select(idx); break; } } diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 07dfd1d9551..fe53923ce8e 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -261,4 +261,34 @@ export function attachProgressBarStyler(widget: IThemable, themeService: IThemeS export function attachStylerCallback(themeService: IThemeService, colors: { [name: string]: ColorIdentifier }, callback: styleFn): IDisposable { return attachStyler(themeService, colors, callback); -} \ No newline at end of file +} + +export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides { + breadcrumbsBackground?: ColorIdentifier; + breadcrumbsItemHoverBackground?: ColorIdentifier; + breadcrumbsItemHoverForeground?: ColorIdentifier; + breadcrumbsItemFocusBackground?: ColorIdentifier; + breadcrumbsItemFocusForeground?: ColorIdentifier; + breadcrumbsActiveItemSelectionBackground?: ColorIdentifier; + breadcrumbsActiveItemSelectionForeground?: ColorIdentifier; + breadcrumbsInactiveItemSelectionBackground?: ColorIdentifier; + breadcrumbsInactiveItemSelectionForeground?: ColorIdentifier; +} + +export const defaultBreadcrumbsStyles = { + breadcrumbsBackground: editorBackground, + breadcrumbsItemHoverBackground: listHoverBackground, + breadcrumbsItemHoverForeground: listHoverForeground, + breadcrumbsItemFocusBackground: listFocusBackground, + breadcrumbsItemFocusForeground: listFocusForeground, + breadcrumbsItemSelectionBackground: listActiveSelectionBackground, + breadcrumbsItemSelectionForeground: listActiveSelectionForeground, + breadcrumbsActiveItemSelectionBackground: listActiveSelectionBackground, + breadcrumbsActiveItemSelectionForeground: listActiveSelectionForeground, + breadcrumbsInactiveItemSelectionBackground: listInactiveSelectionBackground, + breadcrumbsInactiveItemSelectionForeground: listInactiveSelectionForeground, +}; + +export function attachBreadcrumbsStyler(widget: IThemable, themeService: IThemeService, style?: IBreadcrumbsWidgetStyleOverrides): IDisposable { + return attachStyler(themeService, { ...defaultBreadcrumbsStyles, ...style }, widget); +} diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index f9b0c858a38..78cb9a535ad 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -20,7 +20,7 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export const EDITOR_TITLE_HEIGHT = 35; -export const BREAD_CRUMPS_HEIGHT = 30; +export const BREAD_CRUMBS_HEIGHT = 25; export const EDITOR_MIN_DIMENSIONS = new Dimension(220, 70); export const EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY); diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index f3d1bea4c7c..e364774c27f 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -29,6 +29,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isEqual } from 'vs/base/common/resources'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; interface FileElement { name: string; @@ -57,13 +58,16 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { @IEditorService private readonly _editorService: IEditorService, @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IThemeService private readonly _themeService: IThemeService, ) { this._domNode = document.createElement('div'); dom.addClasses(this._domNode, 'editor-breadcrumbs', 'show-file-icons'); + dom.append(container, this._domNode); + this._widget = new BreadcrumbsWidget(this._domNode); this._widget.onDidSelectItem(this._onDidSelectItem, this, this._disposables); this._widget.onDidChangeFocus(val => this._ckBreadcrumbsFocused.set(val), undefined, this._disposables); - container.appendChild(this._domNode); + this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService)); this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); this._ckBreadcrumbsFocused = EditorBreadcrumbs.CK_BreadcrumbsFocused.bindTo(this._contextKeyService); @@ -143,7 +147,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } select(): void { - this._widget.select(); + const item = this._widget.getFocusedItem(); + if (item) { + this._widget.select(item); + } } private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index ce9f5dbf209..4588fb166ab 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -34,7 +34,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceWorker } from 'vs/base/common/async'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; -import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, EDITOR_MIN_DIMENSIONS, EDITOR_MAX_DIMENSIONS, getActiveTextEditorOptions, IEditorOpeningEvent, BREAD_CRUMPS_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, EDITOR_MIN_DIMENSIONS, EDITOR_MAX_DIMENSIONS, getActiveTextEditorOptions, IEditorOpeningEvent, BREAD_CRUMBS_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { join } from 'vs/base/common/paths'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -1373,8 +1373,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to controls this.titleAreaControl.layout(new Dimension(this.dimension.width, EDITOR_TITLE_HEIGHT)); - this.breadcrumbsControl.layout(new Dimension(this.dimension.width, BREAD_CRUMPS_HEIGHT)); - this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + BREAD_CRUMPS_HEIGHT))); + this.breadcrumbsControl.layout(new Dimension(this.dimension.width, BREAD_CRUMBS_HEIGHT)); + this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + BREAD_CRUMBS_HEIGHT))); } toJSON(): ISerializedEditorGroup { diff --git a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css index 36815a8d225..a141046f6bb 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css +++ b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css @@ -3,10 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench>.part.editor>.content .editor-breadcrumbs .monaco-breadcrumbs { - --color-item-focused: pink; -} - -.monaco-workbench>.part.editor>.content .editor-breadcrumbs .monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { - color: var(--color-item-focused); +.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .editor-breadcrumbs { + opacity: .8; } From d50438e6ed2ea2c6b1d0703e5690a35f5124c509 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 10:38:45 +0200 Subject: [PATCH 015/151] tweak styles --- src/vs/platform/theme/common/styler.ts | 2 +- .../browser/parts/editor/editorBreadcrumbs.ts | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index fe53923ce8e..a14be6f3154 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -285,7 +285,7 @@ export const defaultBreadcrumbsStyles = { breadcrumbsItemSelectionForeground: listActiveSelectionForeground, breadcrumbsActiveItemSelectionBackground: listActiveSelectionBackground, breadcrumbsActiveItemSelectionForeground: listActiveSelectionForeground, - breadcrumbsInactiveItemSelectionBackground: listInactiveSelectionBackground, + breadcrumbsInactiveItemSelectionBackground: editorBackground, breadcrumbsInactiveItemSelectionForeground: listInactiveSelectionForeground, }; diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index e364774c27f..b42df4763fe 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -102,7 +102,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { let label = this._instantiationService.createInstance(FileLabel, target, {}); - label.setFile(element.uri, { fileKind: element.kind, hidePath: true }); + label.setFile(element.uri, { fileKind: element.kind, hidePath: true, fileDecorations: { colors: false, badges: false } }); disposables.push(label); }; @@ -111,19 +111,21 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { let path = uri.path; while (true) { - if (workspace && isEqual(workspace.uri, uri, true) || path === '/') { - break; - } - let first = items.length === 0; let name = paths.basename(path); uri = uri.with({ path }); - path = paths.dirname(path); + if (workspace && isEqual(workspace.uri, uri, true)) { + break; + } items.unshift(new RenderedBreadcrumbsItem( render, { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, !first )); + path = paths.dirname(path); + if (path === '/') { + break; + } } this._widget.replace(undefined, items); @@ -168,6 +170,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { res.setInput(item.element.uri.with({ path: paths.dirname(item.element.uri.path) })); res.onDidPickElement(data => { this._contextViewService.hideContextView(); + this._widget.select(undefined); if (!data) { return; } @@ -276,7 +279,11 @@ export class FileRenderer implements IRenderer { } renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void { - templateData.setFile(element.resource, { hidePath: true, fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE }); + templateData.setFile(element.resource, { + hidePath: true, + fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE, + fileDecorations: { colors: true, badges: true } + }); } disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { From af12e2617fcfecfdac10aa38ef0fc8e53e43847a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 10:46:58 +0200 Subject: [PATCH 016/151] add simple sorter --- .../browser/parts/editor/editorBreadcrumbs.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index b42df4763fe..8a4673d45c7 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -19,7 +19,7 @@ import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRe import { FileLabel } from 'vs/workbench/browser/labels'; import { EditorInput } from 'vs/workbench/common/editor'; import { IEditorBreadcrumbs, IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent } from 'vs/base/parts/tree/browser/tree'; +import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent, ISorter } from 'vs/base/parts/tree/browser/tree'; import { TPromise } from 'vs/base/common/winjs.base'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -30,6 +30,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { isEqual } from 'vs/base/common/resources'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; +import { compareFileNames } from 'vs/base/common/comparers'; interface FileElement { name: string; @@ -291,12 +292,27 @@ export class FileRenderer implements IRenderer { } } +export class FileSorter implements ISorter { + compare(tree: ITree, a: IFileStat, b: IFileStat): number { + if (a.isDirectory === b.isDirectory) { + // same type -> compare on names + return compareFileNames(a.name, b.name); + } else if (a.isDirectory) { + return -1; + } else { + return 1; + } + } +} + export class BreadcrumbsFilePicker extends BreadcrumbsPicker { protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { + // todo@joh reuse explorer implementations? config.dataSource = this._instantiationService.createInstance(FileDataSource); config.renderer = this._instantiationService.createInstance(FileRenderer); + config.sorter = new FileSorter(); return config; } From b4325d00ebb31d6a28a0f488d1a256f0f5c261a0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 10:50:59 +0200 Subject: [PATCH 017/151] fix item height --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index be772fca1bb..0a6f4ced2c5 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -14,7 +14,7 @@ .monaco-breadcrumbs .monaco-breadcrumb-item { display: flex; align-items: center; - padding: 3px; + padding: 0 5px 0 3px; flex: 0 1 auto; white-space: nowrap; cursor: pointer; From f8f26b3ae57052eda70285b52793377891a875a4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 11:02:48 +0200 Subject: [PATCH 018/151] fix - set missing background style --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 564a4cd67bf..52fb96f0223 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -132,6 +132,9 @@ export class BreadcrumbsWidget { style(style: IBreadcrumbsWidgetStyles): void { let content = ''; + if (style.breadcrumbsBackground) { + content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`; + } if (style.breadcrumbsItemFocusForeground) { content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsItemFocusForeground}}\n`; } From db1eedb9c7928fd2a157fb3efd86ab23b069db90 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 14:59:57 +0200 Subject: [PATCH 019/151] splice and editor tracking experiements --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 10 +- .../browser/parts/editor/editorBreadcrumbs.ts | 91 +++++++++++++++++-- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 52fb96f0223..e8d2e242514 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -214,14 +214,12 @@ export class BreadcrumbsWidget { this._onDidSelectItem.fire(this._items[this._selectedItemIdx]); } - append(item: BreadcrumbsItem): void { - this._items.push(item); - this._render(this._items.length - 1); + items(): ReadonlyArray { + return this._items; } - replace(existing: BreadcrumbsItem, newItems: BreadcrumbsItem[]): void { - let start = !existing ? 0 : this._items.indexOf(existing); - let removed = this._items.splice(start, this._items.length - start, ...newItems); + splice(start: number, deleteCount: number, items: BreadcrumbsItem[]): void { + let removed = this._items.splice(start, deleteCount, ...items); this._render(start); dispose(removed); } diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 8a4673d45c7..a87c49e86cc 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -19,7 +19,7 @@ import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRe import { FileLabel } from 'vs/workbench/browser/labels'; import { EditorInput } from 'vs/workbench/common/editor'; import { IEditorBreadcrumbs, IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ITreeConfiguration, IDataSource, ITree, IRenderer, ISelectionEvent, ISorter } from 'vs/base/parts/tree/browser/tree'; +import { ITreeConfiguration, IDataSource, IRenderer, ISelectionEvent, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import { TPromise } from 'vs/base/common/winjs.base'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -31,6 +31,8 @@ import { isEqual } from 'vs/base/common/resources'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { compareFileNames } from 'vs/base/common/comparers'; +import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { onUnexpectedError } from 'vs/base/common/errors'; interface FileElement { name: string; @@ -43,12 +45,14 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); static CK_BreadcrumbsFocused = new RawContextKey('breadcrumbsFocused', false); + private readonly _ckBreadcrumbsVisible: IContextKey; + private readonly _ckBreadcrumbsFocused: IContextKey; + private readonly _disposables = new Array(); private readonly _domNode: HTMLDivElement; private readonly _widget: BreadcrumbsWidget; - private readonly _ckBreadcrumbsVisible: IContextKey; - private readonly _ckBreadcrumbsFocused: IContextKey; + private _editorDisposables = new Array(); constructor( container: HTMLElement, @@ -92,6 +96,9 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { openEditor(input: EditorInput): void { + this._editorDisposables = dispose(this._editorDisposables); + + let uri = input.getResource(); if (!uri || !this._fileService.canHandleResource(uri)) { return this.closeEditor(undefined); @@ -100,25 +107,24 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._ckBreadcrumbsVisible.set(true); dom.toggleClass(this._domNode, 'hidden', false); - const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { let label = this._instantiationService.createInstance(FileLabel, target, {}); label.setFile(element.uri, { fileKind: element.kind, hidePath: true, fileDecorations: { colors: false, badges: false } }); disposables.push(label); }; - let items: RenderedBreadcrumbsItem[] = []; + let fileItems: RenderedBreadcrumbsItem[] = []; let workspace = this._workspaceService.getWorkspaceFolder(uri); let path = uri.path; while (true) { - let first = items.length === 0; + let first = fileItems.length === 0; let name = paths.basename(path); uri = uri.with({ path }); if (workspace && isEqual(workspace.uri, uri, true)) { break; } - items.unshift(new RenderedBreadcrumbsItem( + fileItems.unshift(new RenderedBreadcrumbsItem( render, { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, !first @@ -129,7 +135,70 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } } - this._widget.replace(undefined, items); + this._widget.splice(0, undefined, fileItems); + + let control = this._editorGroup.activeControl.getControl() as ICodeEditor; + if (!isCodeEditor(control)) { + return; + } + + let editorWait = new TPromise(resolve => { + if (control.getModel() && control.getModel().uri.toString() === input.getResource().toString()) { + return resolve(true); + } + let listener = control.onDidChangeModel(e => { + if (e.newModelUrl.toString() === input.getResource().toString()) { + resolve(true); + listener.dispose(); + } + }); + this._editorDisposables.push({ + dispose: () => { + resolve(false); + listener.dispose(); + } + }); + }); + + editorWait.then(success => { + console.log(success, control.getModel().uri.path); + if (!success) { + return undefined; + } + // let request = OutlineModel.create(control.getModel()); + // let { promise } = asDisposablePromise(request, undefined, this._editorDisposables); + // return promise; + + // }).then(model => { + // if (!model) { + // console.log('canceled', control.getModel().uri.path); + // return; // canceled + // } + + // let showOutlineForPosition = (position: IPosition) => { + // console.log('show', model.textModel.uri.path); + // let element: OutlineElement | OutlineGroup = model.getItemEnclosingPosition(position); + // let outlineItems: SimpleBreadcrumbsItem[] = []; + // while (element) { + // if (element instanceof OutlineElement) { + // outlineItems.push(new SimpleBreadcrumbsItem(element.symbol.name)); + // } else { + // outlineItems.push(new SimpleBreadcrumbsItem(element.providerIndex.toString())); + // } + // if (element.parent instanceof OutlineElement || element.parent instanceof OutlineGroup) { + // element = element.parent; + // } else { + // element = undefined; + // } + // } + // outlineItems.reverse(); + // this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); + // }; + + // showOutlineForPosition(control.getPosition()); + // debounceEvent(control.onDidChangeCursorPosition, (last, cur) => cur, 100)(_ => showOutlineForPosition(control.getPosition()), undefined, this._editorDisposables); + + }, onUnexpectedError); } closeEditor(input: EditorInput): void { @@ -158,6 +227,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { + if (!(item instanceof RenderedBreadcrumbsItem)) { + return; + } + this._editorGroup.focus(); this._contextViewService.showContextView({ @@ -206,7 +279,7 @@ export abstract class BreadcrumbsPicker { container.appendChild(this._domNode); this._tree = this._instantiationService.createInstance(WorkbenchTree, this._domNode, this._completeTreeConfiguration({ dataSource: undefined }), {}); - debounceEvent(this._tree.onDidChangeSelection, (last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables); + debounceEvent(this._tree.onDidChangeSelection, (_last, cur) => cur, 0)(this._onDidChangeSelection, this, this._disposables); this.focus = dom.trackFocus(this._domNode); this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); From 925faa2f5506f5503fa97fa73327355eab0a0d20 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 16:04:58 +0200 Subject: [PATCH 020/151] fix update of file elements --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index a87c49e86cc..e144273796d 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -98,7 +98,6 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._editorDisposables = dispose(this._editorDisposables); - let uri = input.getResource(); if (!uri || !this._fileService.canHandleResource(uri)) { return this.closeEditor(undefined); @@ -135,7 +134,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } } - this._widget.splice(0, undefined, fileItems); + this._widget.splice(0, this._widget.items().length, fileItems); let control = this._editorGroup.activeControl.getControl() as ICodeEditor; if (!isCodeEditor(control)) { From 1c7032fa2c1448b9166e8181cc198d8d4822e38b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 16:41:00 +0200 Subject: [PATCH 021/151] outline element ftw --- .../browser/parts/editor/editorBreadcrumbs.ts | 114 +++++++++--------- 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index e144273796d..abe74857ea7 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -32,7 +32,11 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { compareFileNames } from 'vs/base/common/comparers'; import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { OutlineModel, OutlineGroup, OutlineElement, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { asDisposablePromise } from 'vs/base/common/async'; +import { IPosition } from 'vs/editor/common/core/position'; +import { first } from 'vs/base/common/collections'; +import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; interface FileElement { name: string; @@ -141,63 +145,63 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { return; } - let editorWait = new TPromise(resolve => { - if (control.getModel() && control.getModel().uri.toString() === input.getResource().toString()) { - return resolve(true); + + let oracle = new class extends Emitter { + + private readonly _listener: IDisposable[] = []; + + constructor() { + super(); + DocumentSymbolProviderRegistry.onDidChange(_ => this.fire()); + this._listener.push(control.onDidChangeModel(_ => this._checkModel())); + this._listener.push(control.onDidChangeModelLanguage(_ => this._checkModel())); + this._checkModel(); } - let listener = control.onDidChangeModel(e => { - if (e.newModelUrl.toString() === input.getResource().toString()) { - resolve(true); - listener.dispose(); + + private _checkModel() { + if (control.getModel() && isEqual(control.getModel().uri, input.getResource())) { + this.fire(); } - }); - this._editorDisposables.push({ - dispose: () => { - resolve(false); - listener.dispose(); + } + + dispose(): void { + dispose(this._listener); + super.dispose(); + } + }; + + this._editorDisposables.push(oracle); + + oracle.event(async _ => { + let model = await asDisposablePromise(OutlineModel.create(control.getModel()), undefined, this._editorDisposables).promise; + if (!model) { + return; + } + type OutlineItem = OutlineElement | OutlineGroup; + + let render = (element: OutlineItem, target: HTMLElement, disposables: IDisposable[]) => { + let label = this._instantiationService.createInstance(FileLabel, target, {}); + if (element instanceof OutlineElement) { + label.setLabel({ name: element.symbol.name }); + } else { + label.setLabel({ name: element.provider.displayName }); } - }); + disposables.push(label); + }; + + let showOutlineForPosition = (position: IPosition) => { + let element = model.getItemEnclosingPosition(position) as TreeElement; + let outlineItems: RenderedBreadcrumbsItem[] = []; + while (element instanceof OutlineGroup || element instanceof OutlineElement) { + outlineItems.unshift(new RenderedBreadcrumbsItem(render, element, !!first(element.children))); + element = element.parent; + } + this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); + }; + + showOutlineForPosition(control.getPosition()); + debounceEvent(control.onDidChangeCursorPosition, (last, cur) => cur, 100)(_ => showOutlineForPosition(control.getPosition()), undefined, this._editorDisposables); }); - - editorWait.then(success => { - console.log(success, control.getModel().uri.path); - if (!success) { - return undefined; - } - // let request = OutlineModel.create(control.getModel()); - // let { promise } = asDisposablePromise(request, undefined, this._editorDisposables); - // return promise; - - // }).then(model => { - // if (!model) { - // console.log('canceled', control.getModel().uri.path); - // return; // canceled - // } - - // let showOutlineForPosition = (position: IPosition) => { - // console.log('show', model.textModel.uri.path); - // let element: OutlineElement | OutlineGroup = model.getItemEnclosingPosition(position); - // let outlineItems: SimpleBreadcrumbsItem[] = []; - // while (element) { - // if (element instanceof OutlineElement) { - // outlineItems.push(new SimpleBreadcrumbsItem(element.symbol.name)); - // } else { - // outlineItems.push(new SimpleBreadcrumbsItem(element.providerIndex.toString())); - // } - // if (element.parent instanceof OutlineElement || element.parent instanceof OutlineGroup) { - // element = element.parent; - // } else { - // element = undefined; - // } - // } - // outlineItems.reverse(); - // this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); - // }; - - // showOutlineForPosition(control.getPosition()); - // debounceEvent(control.onDidChangeCursorPosition, (last, cur) => cur, 100)(_ => showOutlineForPosition(control.getPosition()), undefined, this._editorDisposables); - - }, onUnexpectedError); } closeEditor(input: EditorInput): void { @@ -226,7 +230,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { - if (!(item instanceof RenderedBreadcrumbsItem)) { + if (item.element instanceof TreeElement) { return; } From 4276839d0119a0fe6091787403e621bbe0cf9204 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Jun 2018 16:47:56 +0200 Subject: [PATCH 022/151] leave todo-tag --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index abe74857ea7..4ef921c25c6 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -196,6 +196,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { outlineItems.unshift(new RenderedBreadcrumbsItem(render, element, !!first(element.children))); element = element.parent; } + // todo@joh compare items for equality and only update changed... this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); }; From 71464af4a1e16ffb40c0f20471efa838e3fbfaa1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jun 2018 15:25:47 +0200 Subject: [PATCH 023/151] show outline elements in picker --- .../browser/parts/editor/editorBreadcrumbs.ts | 89 ++++++++++++++----- 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 4ef921c25c6..037240bb87e 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -14,7 +14,7 @@ import * as paths from 'vs/base/common/paths'; import URI from 'vs/base/common/uri'; import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature2 } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { FileLabel } from 'vs/workbench/browser/labels'; import { EditorInput } from 'vs/workbench/common/editor'; @@ -27,23 +27,29 @@ import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, dirname } from 'vs/base/common/resources'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { compareFileNames } from 'vs/base/common/comparers'; import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { OutlineModel, OutlineGroup, OutlineElement, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { asDisposablePromise } from 'vs/base/common/async'; +import { asDisposablePromise, setDisposableTimeout } from 'vs/base/common/async'; import { IPosition } from 'vs/editor/common/core/position'; import { first } from 'vs/base/common/collections'; import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; +import { Range } from 'vs/editor/common/core/range'; +import { OutlineDataSource, OutlineRenderer, OutlineItemComparator, OutlineController } from 'vs/editor/contrib/documentSymbols/outlineTree'; -interface FileElement { - name: string; - uri: URI; - kind: FileKind; +class FileElement { + constructor( + readonly name: string, + readonly kind: FileKind, + readonly uri: URI, + ) { } } +type BreadcrumbElement = FileElement | OutlineGroup | OutlineElement; + export class EditorBreadcrumbs implements IEditorBreadcrumbs { static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); @@ -129,7 +135,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } fileItems.unshift(new RenderedBreadcrumbsItem( render, - { name, uri, kind: first ? FileKind.FILE : FileKind.FOLDER }, + new FileElement(name, first ? FileKind.FILE : FileKind.FOLDER, uri), !first )); path = paths.dirname(path); @@ -155,7 +161,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { DocumentSymbolProviderRegistry.onDidChange(_ => this.fire()); this._listener.push(control.onDidChangeModel(_ => this._checkModel())); this._listener.push(control.onDidChangeModelLanguage(_ => this._checkModel())); - this._checkModel(); + this._listener.push(setDisposableTimeout(_ => this._checkModel(), 0)); } private _checkModel() { @@ -229,23 +235,27 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } } - private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { - - if (item.element instanceof TreeElement) { - return; - } - + private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { this._editorGroup.focus(); + let ctor: IConstructorSignature2; + let input: any; + if (item.element instanceof FileElement) { + ctor = BreadcrumbsFilePicker; + input = dirname(item.element.uri); + } else { + ctor = BreadcrumbsOutlinePicker; + input = item.element.parent; + } + this._contextViewService.showContextView({ getAnchor() { return item.node; }, render: (container: HTMLElement) => { dom.addClasses(container, 'show-file-icons'); - let res = this._instantiationService.createInstance(BreadcrumbsFilePicker, container); + let res = this._instantiationService.createInstance(ctor, container, input); res.layout({ width: 250, height: 300 }); - res.setInput(item.element.uri.with({ path: paths.dirname(item.element.uri.path) })); res.onDidPickElement(data => { this._contextViewService.hideContextView(); this._widget.select(undefined); @@ -253,7 +263,23 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { return; } if (URI.isUri(data)) { + // open new editor this._editorService.openEditor({ resource: data }); + + } else if (data instanceof OutlineElement) { + + let resource: URI; + let candidate = data.parent; + while (candidate) { + if (candidate instanceof OutlineModel) { + resource = candidate.textModel.uri; + break; + } + candidate = candidate.parent; + } + + this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.identifierRange) } }); + } }); return res; @@ -275,6 +301,7 @@ export abstract class BreadcrumbsPicker { constructor( container: HTMLElement, + input: any, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IThemeService protected readonly _themeService: IThemeService, ) { @@ -287,6 +314,9 @@ export abstract class BreadcrumbsPicker { this.focus = dom.trackFocus(this._domNode); this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); + + this._tree.domFocus(); + this._tree.setInput(input); } dispose(): void { @@ -393,11 +423,6 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { return config; } - setInput(resource: URI): void { - this._tree.domFocus(); - this._tree.setInput(resource); - } - protected _onDidChangeSelection(e: ISelectionEvent): void { let [first] = e.selection; let stat = first as IFileStat; @@ -407,6 +432,26 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { } } +export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { + + protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { + config.dataSource = this._instantiationService.createInstance(OutlineDataSource); + config.renderer = this._instantiationService.createInstance(OutlineRenderer); + config.controller = this._instantiationService.createInstance(OutlineController, {}); + config.sorter = new OutlineItemComparator(); + return config; + } + + protected _onDidChangeSelection(e: ISelectionEvent): void { + if (e.payload && !e.payload.didClickElement) { + return; + } + let [first] = e.selection; + if (first instanceof OutlineElement) { + this._onDidPickElement.fire(first); + } + } +} //#region commands From c97de6d4cb9800c9554698805826369d7af9e49e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 18 Jun 2018 15:26:18 +0200 Subject: [PATCH 024/151] outline - add twistie knowledge to event --- src/vs/editor/contrib/documentSymbols/outlineTree.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 078a79f4fa5..b34dcd8dfef 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -310,7 +310,7 @@ export class OutlineController extends WorkbenchTreeController { protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { - const payload = { origin: origin, originalEvent: event }; + const payload = { origin: origin, originalEvent: event, didClickElement: false }; if (tree.getInput() === element) { tree.clearFocus(payload); @@ -322,13 +322,13 @@ export class OutlineController extends WorkbenchTreeController { } event.stopPropagation(); + payload.didClickElement = element instanceof OutlineElement && !this.isClickOnTwistie(event); + tree.domFocus(); tree.setSelection([element], payload); tree.setFocus(element, payload); - const didClickElement = element instanceof OutlineElement && !this.isClickOnTwistie(event); - - if (!didClickElement) { + if (!payload.didClickElement) { if (tree.isExpanded(element)) { tree.collapse(element).then(null, onUnexpectedError); } else { From 162758bd115051ac95cd92b2f0df7e78445aa67c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 29 Jun 2018 15:27:18 +0200 Subject: [PATCH 025/151] adopt api changes --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 037240bb87e..c5f086b0724 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -278,7 +278,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { candidate = candidate.parent; } - this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.identifierRange) } }); + this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.selectionRange) } }); } }); From 977c5c47abb1fae50fe63bcd442e0b8f610bddb9 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 3 Jul 2018 10:25:42 -0700 Subject: [PATCH 026/151] Use HTML headings for viewlet titles. --- src/vs/workbench/browser/media/part.css | 5 ++++- src/vs/workbench/browser/parts/compositePart.ts | 2 +- src/vs/workbench/browser/parts/editor/media/titlecontrol.css | 2 +- src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css | 2 +- test/smoke/src/areas/workbench/viewlet.ts | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index 94f68267136..f9f85749d90 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -30,9 +30,12 @@ padding-left: 12px; } -.monaco-workbench > .part > .title > .title-label span { +.monaco-workbench > .part > .title > .title-label h2 { font-size: 11px; cursor: default; + font-weight: normal; + -webkit-margin-before: 0; + -webkit-margin-after: 0; } .monaco-workbench > .part > .title > .title-label a { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 386e2981306..52578559e00 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -434,7 +434,7 @@ export abstract class CompositePart extends Part { $(parent).div({ 'class': 'title-label' }, div => { - titleLabel = div.span(); + titleLabel = div.element('h2'); }); const $this = this; diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index da2df0dfeca..41e78a153f2 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -21,7 +21,7 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before, .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a, .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label span, +.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label h2, .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span { cursor: pointer; } diff --git a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css index 312dae4c272..9a5f5bd6e00 100644 --- a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css +++ b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css @@ -12,7 +12,7 @@ visibility: hidden !important; } -.monaco-workbench > .sidebar > .title > .title-label span { +.monaco-workbench > .sidebar > .title > .title-label h2 { text-transform: uppercase; } diff --git a/test/smoke/src/areas/workbench/viewlet.ts b/test/smoke/src/areas/workbench/viewlet.ts index 2293752b705..33e5a4549bd 100644 --- a/test/smoke/src/areas/workbench/viewlet.ts +++ b/test/smoke/src/areas/workbench/viewlet.ts @@ -12,6 +12,6 @@ export abstract class Viewlet { constructor(protected code: Code) { } async waitForTitle(fn: (title: string) => boolean): Promise { - await this.code.waitForTextContent('.monaco-workbench-container .part.sidebar > .title > .title-label > span', undefined, fn); + await this.code.waitForTextContent('.monaco-workbench-container .part.sidebar > .title > .title-label > h2', undefined, fn); } } \ No newline at end of file From b7023038d2a42c5d4d681ad54dee7aaf65aeb648 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 3 Jul 2018 11:03:07 -0700 Subject: [PATCH 027/151] Make subsections of viewlets also have heading titles --- src/vs/workbench/browser/parts/views/media/panelviewlet.css | 5 ++++- src/vs/workbench/browser/parts/views/panelViewlet.ts | 2 +- .../workbench/parts/debug/electron-browser/callStackView.ts | 2 +- .../parts/extensions/electron-browser/extensionsViews.ts | 2 +- .../parts/files/electron-browser/views/openEditorsView.ts | 2 +- src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/panelviewlet.css index adddb24399d..c0d8ef43573 100644 --- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/panelviewlet.css @@ -3,8 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-panel-view .panel > .panel-header > .title { +.monaco-panel-view .panel > .panel-header > h3.title { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; + font-size: 11px; + -webkit-margin-before: 0; + -webkit-margin-after: 0; } diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 8bfdfdae01c..d85efdbfb66 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -120,7 +120,7 @@ export abstract class ViewletPanel extends Panel implements IView { } protected renderHeaderTitle(container: HTMLElement): void { - append(container, $('.title', null, this.title)); + append(container, $('h3.title', null, this.title)); } focus(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 8b626653218..68d81977be5 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -88,7 +88,7 @@ export class CallStackView extends TreeViewsViewletPanel { } protected renderHeaderTitle(container: HTMLElement): void { - const title = dom.append(container, $('.title.debug-call-stack-title')); + const title = dom.append(container, $('h3.title.debug-call-stack-title')); const name = dom.append(title, $('span')); name.textContent = this.options.title; this.pauseMessage = dom.append(title, $('span.pause-message')); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index a03d325d251..8675ccbd4cd 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -72,7 +72,7 @@ export class ExtensionsListView extends ViewletPanel { } renderHeader(container: HTMLElement): void { - const titleDiv = append(container, $('div.title')); + const titleDiv = append(container, $('h3.title')); append(titleDiv, $('span')).textContent = this.options.title; this.badgeContainer = append(container, $('.count-badge-wrapper')); diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index e949d6f6fe6..631d9d2008c 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -176,7 +176,7 @@ export class OpenEditorsView extends ViewletPanel { } protected renderHeaderTitle(container: HTMLElement): void { - const title = dom.append(container, $('.title')); + const title = dom.append(container, $('h3.title')); dom.append(title, $('span', null, this.title)); const count = dom.append(container, $('.count')); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index b369614adb0..d68a54ffb8d 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -765,7 +765,7 @@ export class RepositoryPanel extends ViewletPanel { } protected renderHeaderTitle(container: HTMLElement): void { - const header = append(container, $('.title.scm-provider')); + const header = append(container, $('h3.title.scm-provider')); const name = append(header, $('.name')); const title = append(name, $('span.title')); const type = append(name, $('span.type')); From a115ec6f706aebd5ba3f50cba8384224f55d8072 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jul 2018 16:29:40 +0200 Subject: [PATCH 028/151] fix compile issue --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index c5f086b0724..0c9d89d3eaa 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -39,6 +39,7 @@ import { first } from 'vs/base/common/collections'; import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; import { OutlineDataSource, OutlineRenderer, OutlineItemComparator, OutlineController } from 'vs/editor/contrib/documentSymbols/outlineTree'; +import { CancellationToken } from 'vs/base/common/cancellation'; class FileElement { constructor( @@ -179,7 +180,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._editorDisposables.push(oracle); oracle.event(async _ => { - let model = await asDisposablePromise(OutlineModel.create(control.getModel()), undefined, this._editorDisposables).promise; + let model = await asDisposablePromise(OutlineModel.create(control.getModel(), CancellationToken.None), undefined, this._editorDisposables).promise; if (!model) { return; } From a53ae59ce9c8b6695bdf1cb190115ba65690d1fb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jul 2018 17:55:41 +0200 Subject: [PATCH 029/151] outline model work --- .../browser/parts/editor/editorBreadcrumbs.ts | 8 ++ .../parts/editor/editorBreadcrumbsModel.ts | 129 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 0c9d89d3eaa..c1736330fd1 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -40,6 +40,7 @@ import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; import { OutlineDataSource, OutlineRenderer, OutlineItemComparator, OutlineController } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { EditorBreadcrumbsModel } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; class FileElement { constructor( @@ -148,6 +149,13 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._widget.splice(0, this._widget.items().length, fileItems); let control = this._editorGroup.activeControl.getControl() as ICodeEditor; + + let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService); + let listener = model.onDidUpdate(_ => { + console.log(model.getElements()); + }); + this._editorDisposables.push(model, listener); + if (!isCodeEditor(control)) { return; } diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts new file mode 100644 index 00000000000..f19a2cd1905 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { OutlineModel, OutlineGroup, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import URI from 'vs/base/common/uri'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import * as paths from 'vs/base/common/paths'; +import { isEqual } from 'vs/base/common/resources'; +import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { debounceEvent, Event, Emitter } from 'vs/base/common/event'; +import { size } from 'vs/base/common/collections'; + +export class FileElement { + constructor( + readonly uri: URI, + readonly isFile: boolean + ) { } +} + +export type BreadcrumbElement = FileElement | OutlineGroup | OutlineElement; + +export class EditorBreadcrumbsModel { + + private readonly _disposables: IDisposable[] = []; + private readonly _fileElements: FileElement[] = []; + private _outlineDisposables: IDisposable[] = []; + private _outlineModel: OutlineModel; + + private _onDidUpdate = new Emitter(); + readonly onDidUpdate: Event = this._onDidUpdate.event; + + constructor( + private readonly _uri: URI, + private readonly _editor: ICodeEditor | undefined, + @IWorkspaceContextService workspaceService: IWorkspaceContextService, + ) { + this._fileElements = EditorBreadcrumbsModel._getFileElements(this._uri, workspaceService); + this._bindToEditor(); + this._onDidUpdate.fire(this); + } + + dispose(): void { + dispose(this._disposables); + } + + getElements(): ReadonlyArray { + if (!this._editor || !this._outlineModel) { + return this._fileElements; + } + + let item: OutlineGroup | OutlineElement = this._outlineModel.getItemEnclosingPosition(this._editor.getPosition()); + let items: (OutlineGroup | OutlineElement)[] = []; + while (item) { + items.push(item); + let parent = item.parent; + if (parent instanceof OutlineModel) { + break; + } + if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) { + break; + } + item = parent; + } + + return (this._fileElements as BreadcrumbElement[]).concat(items.reverse()); + } + + private static _getFileElements(uri: URI, workspaceService: IWorkspaceContextService): FileElement[] { + let result: FileElement[] = []; + let workspace = workspaceService.getWorkspaceFolder(uri); + let path = uri.path; + while (path !== '/') { + if (workspace && isEqual(workspace.uri, uri)) { + break; + } + result.push(new FileElement(uri, result.length === 0)); + path = paths.dirname(path); + uri = uri.with({ path }); + } + return result.reverse(); + } + + private _bindToEditor(): void { + if (!this._editor) { + return; + } + this._updateOutline(); + this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); + this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline())); + this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); + this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline())); + } + + private _updateOutline(): void { + + this._outlineDisposables = dispose(this._outlineDisposables); + + const model = this._editor.getModel(); + if (!model || !DocumentSymbolProviderRegistry.has(model) || !isEqual(model.uri, this._uri)) { + return; + } + + const source = new CancellationTokenSource(); + + this._outlineDisposables.push({ + dispose: () => { + source.cancel(); + source.dispose(); + } + }); + OutlineModel.create(model, source.token).then(model => { + this._outlineModel = model; + this._onDidUpdate.fire(this); + this._outlineDisposables.push(debounceEvent(this._editor.onDidChangeCursorPosition, _ => _, 250)(_ => this._onDidUpdate.fire(this))); + }).catch(err => { + this._outlineModel = undefined; + this._onDidUpdate.fire(this); + onUnexpectedError(err); + }); + } +} From b60b390330d6b3948bb471505caefd6c089ece21 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 5 Jul 2018 10:09:39 +0200 Subject: [PATCH 030/151] less nervous breadcrumbs model --- .../parts/editor/editorBreadcrumbsModel.ts | 111 ++++++++++++------ 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts index f19a2cd1905..cda2c2252fd 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -5,18 +5,21 @@ 'use strict'; -import { OutlineModel, OutlineGroup, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import URI from 'vs/base/common/uri'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { equals } from 'vs/base/common/arrays'; +import { TimeoutTimer } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { size } from 'vs/base/common/collections'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as paths from 'vs/base/common/paths'; import { isEqual } from 'vs/base/common/resources'; +import URI from 'vs/base/common/uri'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IPosition } from 'vs/editor/common/core/position'; import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { debounceEvent, Event, Emitter } from 'vs/base/common/event'; -import { size } from 'vs/base/common/collections'; +import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export class FileElement { constructor( @@ -31,8 +34,9 @@ export class EditorBreadcrumbsModel { private readonly _disposables: IDisposable[] = []; private readonly _fileElements: FileElement[] = []; + + private _outlineElements: (OutlineGroup | OutlineElement)[] = []; private _outlineDisposables: IDisposable[] = []; - private _outlineModel: OutlineModel; private _onDidUpdate = new Emitter(); readonly onDidUpdate: Event = this._onDidUpdate.event; @@ -52,25 +56,7 @@ export class EditorBreadcrumbsModel { } getElements(): ReadonlyArray { - if (!this._editor || !this._outlineModel) { - return this._fileElements; - } - - let item: OutlineGroup | OutlineElement = this._outlineModel.getItemEnclosingPosition(this._editor.getPosition()); - let items: (OutlineGroup | OutlineElement)[] = []; - while (item) { - items.push(item); - let parent = item.parent; - if (parent instanceof OutlineModel) { - break; - } - if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) { - break; - } - item = parent; - } - - return (this._fileElements as BreadcrumbElement[]).concat(items.reverse()); + return [].concat(this._fileElements, this._outlineElements); } private static _getFileElements(uri: URI, workspaceService: IWorkspaceContextService): FileElement[] { @@ -96,15 +82,18 @@ export class EditorBreadcrumbsModel { this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline())); this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); - this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline())); + this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true))); } - private _updateOutline(): void { + private _updateOutline(didChangeContent?: boolean): void { this._outlineDisposables = dispose(this._outlineDisposables); + if (!didChangeContent) { + this._updateOutlineElements([]); + } - const model = this._editor.getModel(); - if (!model || !DocumentSymbolProviderRegistry.has(model) || !isEqual(model.uri, this._uri)) { + const buffer = this._editor.getModel(); + if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) { return; } @@ -116,14 +105,58 @@ export class EditorBreadcrumbsModel { source.dispose(); } }); - OutlineModel.create(model, source.token).then(model => { - this._outlineModel = model; - this._onDidUpdate.fire(this); - this._outlineDisposables.push(debounceEvent(this._editor.onDidChangeCursorPosition, _ => _, 250)(_ => this._onDidUpdate.fire(this))); + OutlineModel.create(buffer, source.token).then(model => { + this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); + const timeout = new TimeoutTimer(); + const lastVersionId = buffer.getVersionId(); + this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { + timeout.cancelAndSet(() => { + if (lastVersionId === buffer.getVersionId()) { + this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); + } + }, 150); + })); + this._outlineDisposables.push(timeout); }).catch(err => { - this._outlineModel = undefined; - this._onDidUpdate.fire(this); + this._updateOutlineElements([]); onUnexpectedError(err); }); } + + private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineGroup | OutlineElement)[] { + if (!model) { + return []; + } + let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position); + let chain: (OutlineGroup | OutlineElement)[] = []; + while (item) { + chain.push(item); + let parent = item.parent; + if (parent instanceof OutlineModel) { + break; + } + if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) { + break; + } + item = parent; + } + return chain; + } + + private _updateOutlineElements(elements: (OutlineGroup | OutlineElement)[]): void { + if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel.outlineElementEquals)) { + this._outlineElements = elements; + this._onDidUpdate.fire(this); + } + } + + private static outlineElementEquals(a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): boolean { + if (a === b) { + return true; + } else if (!a || !b) { + return false; + } else { + return a.id === b.id; + } + } } From b44d778a82f0cfcb7092ae4d9b3a3ebc76a40639 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 5 Jul 2018 15:02:55 +0200 Subject: [PATCH 031/151] some breadcrumb work --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 65 ++- .../browser/parts/editor/editorBreadcrumbs.ts | 381 ++++++++++-------- .../parts/editor/editorBreadcrumbsModel.ts | 2 +- 3 files changed, 237 insertions(+), 211 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index e8d2e242514..5a77dafaae2 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -13,44 +13,32 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Event, Emitter } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; +import { commonPrefixLength } from 'vs/base/common/arrays'; -export class BreadcrumbsItem { - - constructor( - readonly node: HTMLElement, - readonly more: boolean - ) { - - } - - dispose(): void { - // - } +export abstract class BreadcrumbsItem { + dispose(): void { } + abstract equals(other: BreadcrumbsItem): boolean; + abstract render(container: HTMLElement): void; } export class SimpleBreadcrumbsItem extends BreadcrumbsItem { - constructor(text: string, title: string = text, more: boolean = true) { - super(document.createElement('div'), more); - this.node.innerText = text; - this.node.title = title; - } -} - -export class RenderedBreadcrumbsItem extends BreadcrumbsItem { - - readonly element: E; - private _disposables: IDisposable[] = []; - - constructor(render: (element: E, container: HTMLDivElement, bucket: IDisposable[]) => any, element: E, more: boolean) { - super(document.createElement('div'), more); - this.element = element; - render(element, this.node as HTMLDivElement, this._disposables); + constructor( + readonly text: string, + readonly title: string = text + ) { + super(); } - dispose() { - dispose(this._disposables); - super.dispose(); + equals(other: this) { + return other === this || other instanceof SimpleBreadcrumbsItem && other.text === this.text && other.title === this.title; + } + + render(container: HTMLElement): void { + let node = document.createElement('div'); + node.title = this.title; + node.innerText = this.text; + container.appendChild(node); } } @@ -214,13 +202,10 @@ export class BreadcrumbsWidget { this._onDidSelectItem.fire(this._items[this._selectedItemIdx]); } - items(): ReadonlyArray { - return this._items; - } - - splice(start: number, deleteCount: number, items: BreadcrumbsItem[]): void { - let removed = this._items.splice(start, deleteCount, ...items); - this._render(start); + setItems(items: BreadcrumbsItem[]): void { + let prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b)); + let removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); + this._render(prefix); dispose(removed); } @@ -251,9 +236,9 @@ export class BreadcrumbsWidget { private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void { dom.clearNode(container); - dom.append(container, item.node); + item.render(container); + dom.append(container); dom.addClass(container, 'monaco-breadcrumb-item'); - dom.toggleClass(container, 'monaco-breadcrumb-item-more', item.more); } private _onClick(event: IMouseEvent): void { diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index c1736330fd1..4e83cec3f0f 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -5,52 +5,88 @@ 'use strict'; -import 'vs/css!./media/editorbreadcrumbs'; import * as dom from 'vs/base/browser/dom'; -import { BreadcrumbsWidget, RenderedBreadcrumbsItem } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; +import { BreadcrumbsItem, BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { compareFileNames } from 'vs/base/common/comparers'; +import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import * as paths from 'vs/base/common/paths'; +import { isEqual } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; -import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { IInstantiationService, IConstructorSignature2 } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { FileLabel } from 'vs/workbench/browser/labels'; -import { EditorInput } from 'vs/workbench/common/editor'; -import { IEditorBreadcrumbs, IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ITreeConfiguration, IDataSource, IRenderer, ISelectionEvent, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import { TPromise } from 'vs/base/common/winjs.base'; +import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; +import 'vs/css!./media/editorbreadcrumbs'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { OutlineElement, OutlineGroup, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { isEqual, dirname } from 'vs/base/common/resources'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; -import { compareFileNames } from 'vs/base/common/comparers'; -import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { OutlineModel, OutlineGroup, OutlineElement, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { asDisposablePromise, setDisposableTimeout } from 'vs/base/common/async'; -import { IPosition } from 'vs/editor/common/core/position'; -import { first } from 'vs/base/common/collections'; -import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; -import { Range } from 'vs/editor/common/core/range'; -import { OutlineDataSource, OutlineRenderer, OutlineItemComparator, OutlineController } from 'vs/editor/contrib/documentSymbols/outlineTree'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { EditorBreadcrumbsModel } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { FileLabel } from 'vs/workbench/browser/labels'; +import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IEditorBreadcrumbs, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; + + +class Item extends BreadcrumbsItem { + + private readonly _disposables: IDisposable[] = []; -class FileElement { constructor( - readonly name: string, - readonly kind: FileKind, - readonly uri: URI, - ) { } -} + readonly element: BreadcrumbElement, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { + super(); + } -type BreadcrumbElement = FileElement | OutlineGroup | OutlineElement; + dispose(): void { + dispose(this._disposables); + } + + equals(other: BreadcrumbsItem): boolean { + if (!(other instanceof Item)) { + return false; + } + if (this.element instanceof FileElement && other.element instanceof FileElement) { + return isEqual(this.element.uri, other.element.uri); + } + if (this.element instanceof TreeElement && other.element instanceof TreeElement) { + return this.element.id === other.element.id; + } + return false; + } + + render(container: HTMLElement): void { + if (this.element instanceof FileElement) { + // file/folder + let label = this._instantiationService.createInstance(FileLabel, container, {}); + label.setFile(this.element.uri, { + hidePath: true, + fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER + }); + this._disposables.push(label); + + } else if (this.element instanceof OutlineGroup) { + // provider + let label = new IconLabel(container); + label.setValue(this.element.provider.displayName); + this._disposables.push(label); + + } else if (this.element instanceof OutlineElement) { + // symbol + let label = new IconLabel(container); + label.setValue(this.element.symbol.name); + this._disposables.push(label); + } + } +} export class EditorBreadcrumbs implements IEditorBreadcrumbs { @@ -64,15 +100,13 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { private readonly _domNode: HTMLDivElement; private readonly _widget: BreadcrumbsWidget; - private _editorDisposables = new Array(); + private _breadcrumbsDisposables = new Array(); constructor( container: HTMLElement, private readonly _editorGroup: IEditorGroup, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IFileService private readonly _fileService: IFileService, - @IContextViewService private readonly _contextViewService: IContextViewService, - @IEditorService private readonly _editorService: IEditorService, @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IThemeService private readonly _themeService: IThemeService, @@ -108,116 +142,123 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { openEditor(input: EditorInput): void { - this._editorDisposables = dispose(this._editorDisposables); + this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables); let uri = input.getResource(); if (!uri || !this._fileService.canHandleResource(uri)) { return this.closeEditor(undefined); } - this._ckBreadcrumbsVisible.set(true); dom.toggleClass(this._domNode, 'hidden', false); - - const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { - let label = this._instantiationService.createInstance(FileLabel, target, {}); - label.setFile(element.uri, { fileKind: element.kind, hidePath: true, fileDecorations: { colors: false, badges: false } }); - disposables.push(label); - }; - - let fileItems: RenderedBreadcrumbsItem[] = []; - let workspace = this._workspaceService.getWorkspaceFolder(uri); - let path = uri.path; - - while (true) { - let first = fileItems.length === 0; - let name = paths.basename(path); - uri = uri.with({ path }); - if (workspace && isEqual(workspace.uri, uri, true)) { - break; - } - fileItems.unshift(new RenderedBreadcrumbsItem( - render, - new FileElement(name, first ? FileKind.FILE : FileKind.FOLDER, uri), - !first - )); - path = paths.dirname(path); - if (path === '/') { - break; - } - } - - this._widget.splice(0, this._widget.items().length, fileItems); + this._ckBreadcrumbsVisible.set(true); let control = this._editorGroup.activeControl.getControl() as ICodeEditor; - let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService); - let listener = model.onDidUpdate(_ => { - console.log(model.getElements()); - }); - this._editorDisposables.push(model, listener); + let listener = model.onDidUpdate(_ => this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService)))); + this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService))); - if (!isCodeEditor(control)) { - return; - } + this._breadcrumbsDisposables.push(model, listener); - let oracle = new class extends Emitter { + // const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { + // let label = this._instantiationService.createInstance(FileLabel, target, {}); + // label.setFile(element.uri, { fileKind: element.kind, hidePath: true, fileDecorations: { colors: false, badges: false } }); + // disposables.push(label); + // }; - private readonly _listener: IDisposable[] = []; + // let fileItems: RenderedBreadcrumbsItem[] = []; + // let workspace = this._workspaceService.getWorkspaceFolder(uri); + // let path = uri.path; - constructor() { - super(); - DocumentSymbolProviderRegistry.onDidChange(_ => this.fire()); - this._listener.push(control.onDidChangeModel(_ => this._checkModel())); - this._listener.push(control.onDidChangeModelLanguage(_ => this._checkModel())); - this._listener.push(setDisposableTimeout(_ => this._checkModel(), 0)); - } + // while (true) { + // let first = fileItems.length === 0; + // let name = paths.basename(path); + // uri = uri.with({ path }); + // if (workspace && isEqual(workspace.uri, uri, true)) { + // break; + // } + // fileItems.unshift(new RenderedBreadcrumbsItem( + // render, + // new FileElement(name, first ? FileKind.FILE : FileKind.FOLDER, uri), + // !first + // )); + // path = paths.dirname(path); + // if (path === '/') { + // break; + // } + // } - private _checkModel() { - if (control.getModel() && isEqual(control.getModel().uri, input.getResource())) { - this.fire(); - } - } + // this._widget.splice(0, this._widget.items().length, fileItems); - dispose(): void { - dispose(this._listener); - super.dispose(); - } - }; + // let control = this._editorGroup.activeControl.getControl() as ICodeEditor; - this._editorDisposables.push(oracle); + // let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService); + // let listener = model.onDidUpdate(_ => console.log(model.getElements())); + // console.log(model.getElements()); + // this._breadcrumbsDisposables.push(model, listener); - oracle.event(async _ => { - let model = await asDisposablePromise(OutlineModel.create(control.getModel(), CancellationToken.None), undefined, this._editorDisposables).promise; - if (!model) { - return; - } - type OutlineItem = OutlineElement | OutlineGroup; + // if (!isCodeEditor(control)) { + // return; + // } - let render = (element: OutlineItem, target: HTMLElement, disposables: IDisposable[]) => { - let label = this._instantiationService.createInstance(FileLabel, target, {}); - if (element instanceof OutlineElement) { - label.setLabel({ name: element.symbol.name }); - } else { - label.setLabel({ name: element.provider.displayName }); - } - disposables.push(label); - }; - let showOutlineForPosition = (position: IPosition) => { - let element = model.getItemEnclosingPosition(position) as TreeElement; - let outlineItems: RenderedBreadcrumbsItem[] = []; - while (element instanceof OutlineGroup || element instanceof OutlineElement) { - outlineItems.unshift(new RenderedBreadcrumbsItem(render, element, !!first(element.children))); - element = element.parent; - } - // todo@joh compare items for equality and only update changed... - this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); - }; + // let oracle = new class extends Emitter { - showOutlineForPosition(control.getPosition()); - debounceEvent(control.onDidChangeCursorPosition, (last, cur) => cur, 100)(_ => showOutlineForPosition(control.getPosition()), undefined, this._editorDisposables); - }); + // private readonly _listener: IDisposable[] = []; + + // constructor() { + // super(); + // DocumentSymbolProviderRegistry.onDidChange(_ => this.fire()); + // this._listener.push(control.onDidChangeModel(_ => this._checkModel())); + // this._listener.push(control.onDidChangeModelLanguage(_ => this._checkModel())); + // this._listener.push(setDisposableTimeout(_ => this._checkModel(), 0)); + // } + + // private _checkModel() { + // if (control.getModel() && isEqual(control.getModel().uri, input.getResource())) { + // this.fire(); + // } + // } + + // dispose(): void { + // dispose(this._listener); + // super.dispose(); + // } + // }; + + // this._breadcrumbsDisposables.push(oracle); + + // oracle.event(async _ => { + // let model = await asDisposablePromise(OutlineModel.create(control.getModel(), CancellationToken.None), undefined, this._breadcrumbsDisposables).promise; + // if (!model) { + // return; + // } + // type OutlineItem = OutlineElement | OutlineGroup; + + // let render = (element: OutlineItem, target: HTMLElement, disposables: IDisposable[]) => { + // let label = this._instantiationService.createInstance(FileLabel, target, {}); + // if (element instanceof OutlineElement) { + // label.setLabel({ name: element.symbol.name }); + // } else { + // label.setLabel({ name: element.provider.displayName }); + // } + // disposables.push(label); + // }; + + // let showOutlineForPosition = (position: IPosition) => { + // let element = model.getItemEnclosingPosition(position) as TreeElement; + // let outlineItems: RenderedBreadcrumbsItem[] = []; + // while (element instanceof OutlineGroup || element instanceof OutlineElement) { + // outlineItems.unshift(new RenderedBreadcrumbsItem(render, element, !!first(element.children))); + // element = element.parent; + // } + // // todo@joh compare items for equality and only update changed... + // this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); + // }; + + // showOutlineForPosition(control.getPosition()); + // debounceEvent(control.onDidChangeCursorPosition, (last, cur) => cur, 100)(_ => showOutlineForPosition(control.getPosition()), undefined, this._breadcrumbsDisposables); + // }); } closeEditor(input: EditorInput): void { @@ -244,56 +285,56 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } } - private _onDidSelectItem(item: RenderedBreadcrumbsItem): void { + private _onDidSelectItem(item: Item): void { this._editorGroup.focus(); - let ctor: IConstructorSignature2; - let input: any; - if (item.element instanceof FileElement) { - ctor = BreadcrumbsFilePicker; - input = dirname(item.element.uri); - } else { - ctor = BreadcrumbsOutlinePicker; - input = item.element.parent; - } + // let ctor: IConstructorSignature2; + // let input: any; + // if (item.element instanceof FileElement) { + // ctor = BreadcrumbsFilePicker; + // input = dirname(item.element.uri); + // } else { + // ctor = BreadcrumbsOutlinePicker; + // input = item.element.parent; + // } - this._contextViewService.showContextView({ - getAnchor() { - return item.node; - }, - render: (container: HTMLElement) => { - dom.addClasses(container, 'show-file-icons'); - let res = this._instantiationService.createInstance(ctor, container, input); - res.layout({ width: 250, height: 300 }); - res.onDidPickElement(data => { - this._contextViewService.hideContextView(); - this._widget.select(undefined); - if (!data) { - return; - } - if (URI.isUri(data)) { - // open new editor - this._editorService.openEditor({ resource: data }); + // this._contextViewService.showContextView({ + // getAnchor() { + // return item.node; + // }, + // render: (container: HTMLElement) => { + // dom.addClasses(container, 'show-file-icons'); + // let res = this._instantiationService.createInstance(ctor, container, input); + // res.layout({ width: 250, height: 300 }); + // res.onDidPickElement(data => { + // this._contextViewService.hideContextView(); + // this._widget.select(undefined); + // if (!data) { + // return; + // } + // if (URI.isUri(data)) { + // // open new editor + // this._editorService.openEditor({ resource: data }); - } else if (data instanceof OutlineElement) { + // } else if (data instanceof OutlineElement) { - let resource: URI; - let candidate = data.parent; - while (candidate) { - if (candidate instanceof OutlineModel) { - resource = candidate.textModel.uri; - break; - } - candidate = candidate.parent; - } + // let resource: URI; + // let candidate = data.parent; + // while (candidate) { + // if (candidate instanceof OutlineModel) { + // resource = candidate.textModel.uri; + // break; + // } + // candidate = candidate.parent; + // } - this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.selectionRange) } }); + // this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.selectionRange) } }); - } - }); - return res; - }, - }); + // } + // }); + // return res; + // }, + // }); } } diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts index cda2c2252fd..913175259ac 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -140,7 +140,7 @@ export class EditorBreadcrumbsModel { } item = parent; } - return chain; + return chain.reverse(); } private _updateOutlineElements(elements: (OutlineGroup | OutlineElement)[]): void { From 4adbb0558c7884ef322ae7ec4ca5dbc47ac77d96 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 5 Jul 2018 15:50:49 +0200 Subject: [PATCH 032/151] add tests for breadcrumbs model --- .../editor/editorBreadcrumbModel.test.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts diff --git a/src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts new file mode 100644 index 00000000000..c0942cd5dc4 --- /dev/null +++ b/src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import URI from 'vs/base/common/uri'; +import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; + + +suite('Breadcrumb Model', function () { + + const workspaceService = new TestContextService(new Workspace('ffff', 'Test', [new WorkspaceFolder({ uri: URI.parse('foo:/bar/baz/ws'), name: 'ws', index: 0 })])); + + test('only uri, inside workspace', function () { + + let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, workspaceService); + let elements = model.getElements(); + + assert.equal(elements.length, 3); + let [one, two, three] = elements as FileElement[]; + assert.equal(one.isFile, false); + assert.equal(two.isFile, false); + assert.equal(three.isFile, true); + assert.equal(one.uri.toString(), 'foo:/bar/baz/ws/some'); + assert.equal(two.uri.toString(), 'foo:/bar/baz/ws/some/path'); + assert.equal(three.uri.toString(), 'foo:/bar/baz/ws/some/path/file.ts'); + }); + + test('only uri, outside workspace', function () { + + let model = new EditorBreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, workspaceService); + let elements = model.getElements(); + + assert.equal(elements.length, 2); + let [one, two] = elements as FileElement[]; + assert.equal(one.isFile, false); + assert.equal(two.isFile, true); + assert.equal(one.uri.toString(), 'foo:/outside'); + assert.equal(two.uri.toString(), 'foo:/outside/file.ts'); + }); +}); From 326d55451dc5c43c5e1bfb9cc14d703fe57dd8e2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 5 Jul 2018 16:49:53 +0200 Subject: [PATCH 033/151] move height into control --- src/vs/workbench/browser/parts/editor/editor.ts | 1 - src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 4 ++++ src/vs/workbench/browser/parts/editor/editorGroupView.ts | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 5581eccbd48..5e63f57bfad 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -20,7 +20,6 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export const EDITOR_TITLE_HEIGHT = 35; -export const BREAD_CRUMBS_HEIGHT = 25; export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70); export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY); diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 4e83cec3f0f..d012ecf2a37 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -130,6 +130,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._ckBreadcrumbsVisible.reset(); } + getPreferredHeight(): number { + return 25; + } + layout(dim: dom.Dimension): void { this._domNode.style.width = `${dim.width}px`; this._domNode.style.height = `${dim.height}px`; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 76b79f57032..39385de58cb 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -34,7 +34,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceWorker } from 'vs/base/common/async'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; -import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, BREAD_CRUMBS_HEIGHT, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { join } from 'vs/base/common/paths'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -1376,8 +1376,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to controls this.titleAreaControl.layout(new Dimension(this.dimension.width, EDITOR_TITLE_HEIGHT)); - this.breadcrumbsControl.layout(new Dimension(this.dimension.width, BREAD_CRUMBS_HEIGHT)); - this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + BREAD_CRUMBS_HEIGHT))); + this.breadcrumbsControl.layout(new Dimension(this.dimension.width, this.breadcrumbsControl.getPreferredHeight())); + this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + this.breadcrumbsControl.getPreferredHeight()))); } toJSON(): ISerializedEditorGroup { From 980b4070cbc6f85383c03b9e19c33e2dcbef395a Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 6 Jul 2018 10:58:28 -0700 Subject: [PATCH 034/151] Move header logic to parent class --- .../workbench/browser/parts/views/media/panelviewlet.css | 1 + src/vs/workbench/browser/parts/views/panelViewlet.ts | 6 +++--- .../parts/debug/electron-browser/callStackView.ts | 9 +++++---- .../files/electron-browser/views/openEditorsView.ts | 8 ++++---- .../workbench/parts/scm/electron-browser/scmViewlet.ts | 7 ++++--- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/panelviewlet.css index c0d8ef43573..1dadcbfc4dd 100644 --- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/panelviewlet.css @@ -10,4 +10,5 @@ font-size: 11px; -webkit-margin-before: 0; -webkit-margin-after: 0; + display: flex; } diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index d85efdbfb66..5712e6218b5 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -100,7 +100,7 @@ export abstract class ViewletPanel extends Panel implements IView { protected renderHeader(container: HTMLElement): void { this.headerContainer = container; - this.renderHeaderTitle(container); + this.renderHeaderTitle(container, [], [this.title]); const actions = append(container, $('.actions')); this.toolbar = new ToolBar(actions, this.contextMenuService, { @@ -119,8 +119,8 @@ export abstract class ViewletPanel extends Panel implements IView { this.updateActionsVisibility(); } - protected renderHeaderTitle(container: HTMLElement): void { - append(container, $('h3.title', null, this.title)); + protected renderHeaderTitle(container: HTMLElement, classList: string[], children: (string | Node)[]): HTMLElement { + return append(container, $('h3.' + ['title', ...classList].join('.'), null, ...children)); } focus(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 68d81977be5..fcf04a518e8 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -87,13 +87,14 @@ export class CallStackView extends TreeViewsViewletPanel { }, 50); } - protected renderHeaderTitle(container: HTMLElement): void { - const title = dom.append(container, $('h3.title.debug-call-stack-title')); - const name = dom.append(title, $('span')); + protected renderHeaderTitle(container: HTMLElement): HTMLElement { + const name = $('span'); name.textContent = this.options.title; - this.pauseMessage = dom.append(title, $('span.pause-message')); + this.pauseMessage = $('span.pause-message'); this.pauseMessage.hidden = true; this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); + + return super.renderHeaderTitle(container, ['debug-call-stack-title'], [name, this.pauseMessage]); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 631d9d2008c..21fbc921193 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -175,11 +175,10 @@ export class OpenEditorsView extends ViewletPanel { })); } - protected renderHeaderTitle(container: HTMLElement): void { - const title = dom.append(container, $('h3.title')); - dom.append(title, $('span', null, this.title)); + protected renderHeaderTitle(container: HTMLElement): HTMLElement { - const count = dom.append(container, $('.count')); + const title = $('span', null, this.title); + const count = $('.count'); this.dirtyCountElement = dom.append(count, $('.monaco-count-badge')); this.disposables.push((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { @@ -196,6 +195,7 @@ export class OpenEditorsView extends ViewletPanel { }))); this.updateDirtyIndicator(); + return super.renderHeaderTitle(container, [], [title, count]); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index d68a54ffb8d..7143fea2b29 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -764,9 +764,8 @@ export class RepositoryPanel extends ViewletPanel { this.menus.onDidChangeTitle(this.updateActions, this, this.disposables); } - protected renderHeaderTitle(container: HTMLElement): void { - const header = append(container, $('h3.title.scm-provider')); - const name = append(header, $('.name')); + protected renderHeaderTitle(container: HTMLElement): HTMLElement { + const name = $('.name'); const title = append(name, $('span.title')); const type = append(name, $('span.type')); @@ -780,6 +779,8 @@ export class RepositoryPanel extends ViewletPanel { const onContextMenu = mapEvent(stop(domEvent(container, 'contextmenu')), e => new StandardMouseEvent(e)); onContextMenu(this.onContextMenu, this, this.disposables); + + return super.renderHeaderTitle(container, ['scm-provider'], [name]); } private onContextMenu(event: StandardMouseEvent): void { From 46ef667d8d823c80c40139cd5a3d1b52fda5e3d3 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 6 Jul 2018 11:44:58 -0700 Subject: [PATCH 035/151] Increse overrides of renderHeaderTitle vs renderHeader --- .../parts/extensions/electron-browser/extensionsViews.ts | 9 +++++---- .../extensions/electron-browser/media/extensions.css | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 8675ccbd4cd..94d79179504 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -71,13 +71,14 @@ export class ExtensionsListView extends ViewletPanel { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); } - renderHeader(container: HTMLElement): void { - const titleDiv = append(container, $('h3.title')); - append(titleDiv, $('span')).textContent = this.options.title; + renderHeaderTitle(container: HTMLElement): HTMLElement { + let title = $('span'); + title.textContent = this.options.title; - this.badgeContainer = append(container, $('.count-badge-wrapper')); + this.badgeContainer = $('.count-badge-wrapper'); this.badge = new CountBadge(this.badgeContainer); this.disposables.push(attachBadgeStyler(this.badge, this.themeService)); + return super.renderHeaderTitle(container, [], [title, this.badgeContainer]); } renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css index 848c0382f9a..a879a47e71a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css @@ -6,4 +6,9 @@ .monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.extensions { -webkit-mask: url('extensions-dark.svg') no-repeat 50% 50%; -webkit-mask-size: 21px; +} + +.extensions .split-view-view .panel-header .count-badge-wrapper { + position: absolute; + right: 12px; } \ No newline at end of file From dddb7aa69f7dd57be5f84151f7abab56968c3898 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 6 Jul 2018 12:52:49 -0700 Subject: [PATCH 036/151] Remove returns --- src/vs/workbench/browser/parts/views/panelViewlet.ts | 4 ++-- .../workbench/parts/debug/electron-browser/callStackView.ts | 4 ++-- .../parts/extensions/electron-browser/extensionsViews.ts | 4 ++-- .../parts/files/electron-browser/views/openEditorsView.ts | 4 ++-- src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 5712e6218b5..86886c0ea7e 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -119,8 +119,8 @@ export abstract class ViewletPanel extends Panel implements IView { this.updateActionsVisibility(); } - protected renderHeaderTitle(container: HTMLElement, classList: string[], children: (string | Node)[]): HTMLElement { - return append(container, $('h3.' + ['title', ...classList].join('.'), null, ...children)); + protected renderHeaderTitle(container: HTMLElement, classList: string[], children: (string | Node)[]): void { + append(container, $('h3.' + ['title', ...classList].join('.'), null, ...children)); } focus(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index fcf04a518e8..cd6b2300ab5 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -87,14 +87,14 @@ export class CallStackView extends TreeViewsViewletPanel { }, 50); } - protected renderHeaderTitle(container: HTMLElement): HTMLElement { + protected renderHeaderTitle(container: HTMLElement): void { const name = $('span'); name.textContent = this.options.title; this.pauseMessage = $('span.pause-message'); this.pauseMessage.hidden = true; this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); - return super.renderHeaderTitle(container, ['debug-call-stack-title'], [name, this.pauseMessage]); + super.renderHeaderTitle(container, ['debug-call-stack-title'], [name, this.pauseMessage]); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 94d79179504..5075965732d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -71,14 +71,14 @@ export class ExtensionsListView extends ViewletPanel { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); } - renderHeaderTitle(container: HTMLElement): HTMLElement { + renderHeaderTitle(container: HTMLElement): void { let title = $('span'); title.textContent = this.options.title; this.badgeContainer = $('.count-badge-wrapper'); this.badge = new CountBadge(this.badgeContainer); this.disposables.push(attachBadgeStyler(this.badge, this.themeService)); - return super.renderHeaderTitle(container, [], [title, this.badgeContainer]); + super.renderHeaderTitle(container, [], [title, this.badgeContainer]); } renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 21fbc921193..ace6268d637 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -175,7 +175,7 @@ export class OpenEditorsView extends ViewletPanel { })); } - protected renderHeaderTitle(container: HTMLElement): HTMLElement { + protected renderHeaderTitle(container: HTMLElement): void { const title = $('span', null, this.title); const count = $('.count'); @@ -195,7 +195,7 @@ export class OpenEditorsView extends ViewletPanel { }))); this.updateDirtyIndicator(); - return super.renderHeaderTitle(container, [], [title, count]); + super.renderHeaderTitle(container, [], [title, count]); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 7143fea2b29..61fb04bd5ff 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -764,7 +764,7 @@ export class RepositoryPanel extends ViewletPanel { this.menus.onDidChangeTitle(this.updateActions, this, this.disposables); } - protected renderHeaderTitle(container: HTMLElement): HTMLElement { + protected renderHeaderTitle(container: HTMLElement): void { const name = $('.name'); const title = append(name, $('span.title')); const type = append(name, $('span.type')); @@ -780,7 +780,7 @@ export class RepositoryPanel extends ViewletPanel { const onContextMenu = mapEvent(stop(domEvent(container, 'contextmenu')), e => new StandardMouseEvent(e)); onContextMenu(this.onContextMenu, this, this.disposables); - return super.renderHeaderTitle(container, ['scm-provider'], [name]); + super.renderHeaderTitle(container, ['scm-provider'], [name]); } private onContextMenu(event: StandardMouseEvent): void { From fb88148b29fff04473d354925b0dfae7b651f7b3 Mon Sep 17 00:00:00 2001 From: Itamar Kestenbaum Date: Sat, 7 Jul 2018 01:24:54 +0300 Subject: [PATCH 037/151] Close param hints if you reach out of bounds of list instead of cycling --- .../editor/contrib/parameterHints/parameterHintsWidget.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 4392d12f485..5a0de6b72b6 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -451,8 +451,10 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { next(): boolean { const length = this.hints.signatures.length; + const last = (this.currentSignature % length) === (length - 1); - if (length < 2) { + // If there is only one signature, or we're on last signature of list + if (length < 2 || last) { this.cancel(); return false; } @@ -464,8 +466,9 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { previous(): boolean { const length = this.hints.signatures.length; + const first = this.currentSignature === 0; - if (length < 2) { + if (length < 2 || first) { this.cancel(); return false; } From a5266b1d6a7d7059af517262038071c69c493c1f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 10:28:19 +0200 Subject: [PATCH 038/151] use collpase icons to separate elements --- .../ui/breadcrumbs/breadcrumbsWidget.css | 18 ++++++++++++++++-- .../base/browser/ui/breadcrumbs/collapsed.svg | 1 + .../browser/ui/breadcrumbs/collpased-dark.svg | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100755 src/vs/base/browser/ui/breadcrumbs/collapsed.svg create mode 100755 src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 0a6f4ced2c5..df5e37ddf8c 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -14,10 +14,9 @@ .monaco-breadcrumbs .monaco-breadcrumb-item { display: flex; align-items: center; - padding: 0 5px 0 3px; flex: 0 1 auto; white-space: nowrap; - cursor: pointer; + cursor: default; align-self: center; height: 100%; } @@ -25,3 +24,18 @@ .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ padding-left: 8px; } + +.monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after { + background-image: url(./collapsed.svg); + width: 16px; + height: 16px; + display: inline-block; + background-size: 16px; + background-position: 50% 50%; + content: ' '; +} + +.vs-dark .monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after { + background-image: url(./collpased-dark.svg); + +} diff --git a/src/vs/base/browser/ui/breadcrumbs/collapsed.svg b/src/vs/base/browser/ui/breadcrumbs/collapsed.svg new file mode 100755 index 00000000000..3a63808c358 --- /dev/null +++ b/src/vs/base/browser/ui/breadcrumbs/collapsed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg b/src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg new file mode 100755 index 00000000000..cf5c3641aa7 --- /dev/null +++ b/src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file From 26da10144c44d4301f7936d20be62e25b4d5395e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 10:33:50 +0200 Subject: [PATCH 039/151] less styles for breadcrumbs --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 5a77dafaae2..aea7bef9669 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -44,14 +44,8 @@ export class SimpleBreadcrumbsItem extends BreadcrumbsItem { export interface IBreadcrumbsWidgetStyles { breadcrumbsBackground?: Color; - breadcrumbsItemHoverBackground?: Color; - breadcrumbsItemHoverForeground?: Color; - breadcrumbsItemFocusBackground?: Color; - breadcrumbsItemFocusForeground?: Color; - breadcrumbsActiveItemSelectionBackground?: Color; - breadcrumbsActiveItemSelectionForeground?: Color; - breadcrumbsInactiveItemSelectionBackground?: Color; - breadcrumbsInactiveItemSelectionForeground?: Color; + breadcrumbsActiveForeground?: Color; + breadcrumbsInactiveForeground?: Color; } export class BreadcrumbsWidget { @@ -123,29 +117,11 @@ export class BreadcrumbsWidget { if (style.breadcrumbsBackground) { content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`; } - if (style.breadcrumbsItemFocusForeground) { - content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsItemFocusForeground}}\n`; + if (style.breadcrumbsActiveForeground) { + content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item { color: ${style.breadcrumbsActiveForeground}}\n`; } - if (style.breadcrumbsItemFocusBackground) { - content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item.focused { background-color: ${style.breadcrumbsItemFocusBackground}}\n`; - } - if (style.breadcrumbsItemFocusForeground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover { color: ${style.breadcrumbsItemHoverForeground}}\n`; - } - if (style.breadcrumbsItemFocusBackground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover { background-color: ${style.breadcrumbsItemHoverBackground}}\n`; - } - if (style.breadcrumbsActiveItemSelectionForeground) { - content += `.monaco-breadcrumbs:hover .monaco-breadcrumb-item.selected { color: ${style.breadcrumbsActiveItemSelectionForeground}}\n`; - } - if (style.breadcrumbsActiveItemSelectionBackground) { - content += `.monaco-breadcrumbs:hover .monaco-breadcrumb-item.selected { background-color: ${style.breadcrumbsActiveItemSelectionBackground}}\n`; - } - if (style.breadcrumbsInactiveItemSelectionForeground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item.selected { color: ${style.breadcrumbsInactiveItemSelectionForeground}}\n`; - } - if (style.breadcrumbsInactiveItemSelectionBackground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item.selected { background-color: ${style.breadcrumbsInactiveItemSelectionBackground}}\n`; + if (style.breadcrumbsInactiveForeground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsInactiveForeground}}\n`; } if (this._styleElement.innerHTML !== content) { this._styleElement.innerHTML = content; From 1b0464601ac759db4215e4aab3336a074450a806 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 10:36:23 +0200 Subject: [PATCH 040/151] :lipstick: --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css | 4 ---- .../browser/parts/editor/media/editorbreadcrumbs.css | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index df5e37ddf8c..08b25c9bc4e 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -21,10 +21,6 @@ height: 100%; } -.monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ - padding-left: 8px; -} - .monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after { background-image: url(./collapsed.svg); width: 16px; diff --git a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css index a141046f6bb..e3b45ae4166 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css +++ b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css @@ -6,3 +6,8 @@ .monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .editor-breadcrumbs { opacity: .8; } + + +.monaco-workbench>.part.editor>.content .editor-group-container .editor-breadcrumbs .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ + padding-left: 8px; +} From 559c2929e0d790b41109f7e52aad55228da53e00 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 10:59:41 +0200 Subject: [PATCH 041/151] wire up pickers --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 17 +- .../browser/parts/editor/editorBreadcrumbs.ts | 210 +++++------------- 2 files changed, 69 insertions(+), 158 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index aea7bef9669..0ed02f6e1b1 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -48,6 +48,11 @@ export interface IBreadcrumbsWidgetStyles { breadcrumbsInactiveForeground?: Color; } +export interface IBreadcrumbsItemEvent { + item: BreadcrumbsItem; + node: HTMLElement; +} + export class BreadcrumbsWidget { private readonly _disposables = new Array(); @@ -55,12 +60,12 @@ export class BreadcrumbsWidget { private readonly _styleElement: HTMLStyleElement; private readonly _scrollable: DomScrollableElement; - private readonly _onDidSelectItem = new Emitter(); - private readonly _onDidFocusItem = new Emitter(); + private readonly _onDidSelectItem = new Emitter(); + private readonly _onDidFocusItem = new Emitter(); private readonly _onDidChangeFocus = new Emitter(); - readonly onDidSelectItem: Event = this._onDidSelectItem.event; - readonly onDidFocusItem: Event = this._onDidFocusItem.event; + readonly onDidSelectItem: Event = this._onDidSelectItem.event; + readonly onDidFocusItem: Event = this._onDidFocusItem.event; readonly onDidChangeFocus: Event = this._onDidChangeFocus.event; private readonly _items = new Array(); @@ -153,7 +158,7 @@ export class BreadcrumbsWidget { this._focusedItemIdx = nth; dom.addClass(this._nodes[this._focusedItemIdx], 'focused'); this._scrollable.setScrollPosition({ scrollLeft: this._nodes[this._focusedItemIdx].offsetLeft }); - this._onDidFocusItem.fire(this._items[this._focusedItemIdx]); + this._onDidFocusItem.fire({ item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx] }); return true; } @@ -175,7 +180,7 @@ export class BreadcrumbsWidget { } this._selectedItemIdx = nth; dom.addClass(this._nodes[this._selectedItemIdx], 'selected'); - this._onDidSelectItem.fire(this._items[this._selectedItemIdx]); + this._onDidSelectItem.fire({ item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx] }); } setItems(items: BreadcrumbsItem[]): void { diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index d012ecf2a37..c824ecef19d 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -6,23 +6,25 @@ 'use strict'; import * as dom from 'vs/base/browser/dom'; -import { BreadcrumbsItem, BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; +import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { compareFileNames } from 'vs/base/common/comparers'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, dirname } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./media/editorbreadcrumbs'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { OutlineElement, OutlineGroup, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { Range } from 'vs/editor/common/core/range'; +import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature2, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; @@ -32,6 +34,7 @@ import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; import { EditorInput } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorBreadcrumbs, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -106,6 +109,8 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { container: HTMLElement, private readonly _editorGroup: IEditorGroup, @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IContextViewService private readonly _contextViewService: IContextViewService, + @IEditorService private readonly _editorService: IEditorService, @IFileService private readonly _fileService: IFileService, @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -162,107 +167,6 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService))); this._breadcrumbsDisposables.push(model, listener); - - - // const render = (element: FileElement, target: HTMLElement, disposables: IDisposable[]) => { - // let label = this._instantiationService.createInstance(FileLabel, target, {}); - // label.setFile(element.uri, { fileKind: element.kind, hidePath: true, fileDecorations: { colors: false, badges: false } }); - // disposables.push(label); - // }; - - // let fileItems: RenderedBreadcrumbsItem[] = []; - // let workspace = this._workspaceService.getWorkspaceFolder(uri); - // let path = uri.path; - - // while (true) { - // let first = fileItems.length === 0; - // let name = paths.basename(path); - // uri = uri.with({ path }); - // if (workspace && isEqual(workspace.uri, uri, true)) { - // break; - // } - // fileItems.unshift(new RenderedBreadcrumbsItem( - // render, - // new FileElement(name, first ? FileKind.FILE : FileKind.FOLDER, uri), - // !first - // )); - // path = paths.dirname(path); - // if (path === '/') { - // break; - // } - // } - - // this._widget.splice(0, this._widget.items().length, fileItems); - - // let control = this._editorGroup.activeControl.getControl() as ICodeEditor; - - // let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService); - // let listener = model.onDidUpdate(_ => console.log(model.getElements())); - // console.log(model.getElements()); - // this._breadcrumbsDisposables.push(model, listener); - - // if (!isCodeEditor(control)) { - // return; - // } - - - // let oracle = new class extends Emitter { - - // private readonly _listener: IDisposable[] = []; - - // constructor() { - // super(); - // DocumentSymbolProviderRegistry.onDidChange(_ => this.fire()); - // this._listener.push(control.onDidChangeModel(_ => this._checkModel())); - // this._listener.push(control.onDidChangeModelLanguage(_ => this._checkModel())); - // this._listener.push(setDisposableTimeout(_ => this._checkModel(), 0)); - // } - - // private _checkModel() { - // if (control.getModel() && isEqual(control.getModel().uri, input.getResource())) { - // this.fire(); - // } - // } - - // dispose(): void { - // dispose(this._listener); - // super.dispose(); - // } - // }; - - // this._breadcrumbsDisposables.push(oracle); - - // oracle.event(async _ => { - // let model = await asDisposablePromise(OutlineModel.create(control.getModel(), CancellationToken.None), undefined, this._breadcrumbsDisposables).promise; - // if (!model) { - // return; - // } - // type OutlineItem = OutlineElement | OutlineGroup; - - // let render = (element: OutlineItem, target: HTMLElement, disposables: IDisposable[]) => { - // let label = this._instantiationService.createInstance(FileLabel, target, {}); - // if (element instanceof OutlineElement) { - // label.setLabel({ name: element.symbol.name }); - // } else { - // label.setLabel({ name: element.provider.displayName }); - // } - // disposables.push(label); - // }; - - // let showOutlineForPosition = (position: IPosition) => { - // let element = model.getItemEnclosingPosition(position) as TreeElement; - // let outlineItems: RenderedBreadcrumbsItem[] = []; - // while (element instanceof OutlineGroup || element instanceof OutlineElement) { - // outlineItems.unshift(new RenderedBreadcrumbsItem(render, element, !!first(element.children))); - // element = element.parent; - // } - // // todo@joh compare items for equality and only update changed... - // this._widget.splice(fileItems.length, this._widget.items().length - fileItems.length, outlineItems); - // }; - - // showOutlineForPosition(control.getPosition()); - // debounceEvent(control.onDidChangeCursorPosition, (last, cur) => cur, 100)(_ => showOutlineForPosition(control.getPosition()), undefined, this._breadcrumbsDisposables); - // }); } closeEditor(input: EditorInput): void { @@ -289,56 +193,48 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } } - private _onDidSelectItem(item: Item): void { + private _onDidSelectItem(event: IBreadcrumbsItemEvent): void { this._editorGroup.focus(); - // let ctor: IConstructorSignature2; - // let input: any; - // if (item.element instanceof FileElement) { - // ctor = BreadcrumbsFilePicker; - // input = dirname(item.element.uri); - // } else { - // ctor = BreadcrumbsOutlinePicker; - // input = item.element.parent; - // } + this._contextViewService.showContextView({ + getAnchor() { + return event.node; + }, + render: (container: HTMLElement) => { + dom.addClasses(container, 'show-file-icons'); + let { element } = event.item as Item; + let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; + let res = this._instantiationService.createInstance(ctor, container, element); + res.layout({ width: 250, height: 300 }); + res.onDidPickElement(data => { + this._contextViewService.hideContextView(); + this._widget.select(undefined); + if (!data) { + return; + } + if (URI.isUri(data)) { + // open new editor + this._editorService.openEditor({ resource: data }); - // this._contextViewService.showContextView({ - // getAnchor() { - // return item.node; - // }, - // render: (container: HTMLElement) => { - // dom.addClasses(container, 'show-file-icons'); - // let res = this._instantiationService.createInstance(ctor, container, input); - // res.layout({ width: 250, height: 300 }); - // res.onDidPickElement(data => { - // this._contextViewService.hideContextView(); - // this._widget.select(undefined); - // if (!data) { - // return; - // } - // if (URI.isUri(data)) { - // // open new editor - // this._editorService.openEditor({ resource: data }); + } else if (data instanceof OutlineElement) { - // } else if (data instanceof OutlineElement) { + let resource: URI; + let candidate = data.parent; + while (candidate) { + if (candidate instanceof OutlineModel) { + resource = candidate.textModel.uri; + break; + } + candidate = candidate.parent; + } - // let resource: URI; - // let candidate = data.parent; - // while (candidate) { - // if (candidate instanceof OutlineModel) { - // resource = candidate.textModel.uri; - // break; - // } - // candidate = candidate.parent; - // } + this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.selectionRange) } }); - // this._editorService.openEditor({ resource, options: { selection: Range.collapseToStart(data.symbol.selectionRange) } }); - - // } - // }); - // return res; - // }, - // }); + } + }); + return res; + }, + }); } } @@ -355,7 +251,7 @@ export abstract class BreadcrumbsPicker { constructor( container: HTMLElement, - input: any, + input: BreadcrumbElement, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IThemeService protected readonly _themeService: IThemeService, ) { @@ -370,7 +266,7 @@ export abstract class BreadcrumbsPicker { this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); this._tree.domFocus(); - this._tree.setInput(input); + this._tree.setInput(this._getInput(input)); } dispose(): void { @@ -386,6 +282,7 @@ export abstract class BreadcrumbsPicker { this._tree.layout(dim.height, dim.width); } + protected abstract _getInput(input: BreadcrumbElement): any; protected abstract _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration; protected abstract _onDidChangeSelection(e: any): void; } @@ -469,6 +366,11 @@ export class FileSorter implements ISorter { export class BreadcrumbsFilePicker extends BreadcrumbsPicker { + protected _getInput(input: BreadcrumbElement): any { + let { uri } = (input as FileElement); + return dirname(uri); + } + protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { // todo@joh reuse explorer implementations? config.dataSource = this._instantiationService.createInstance(FileDataSource); @@ -488,6 +390,10 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { + protected _getInput(input: BreadcrumbElement): any { + return (input as TreeElement).parent; + } + protected _completeTreeConfiguration(config: ITreeConfiguration): ITreeConfiguration { config.dataSource = this._instantiationService.createInstance(OutlineDataSource); config.renderer = this._instantiationService.createInstance(OutlineRenderer); @@ -497,7 +403,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } protected _onDidChangeSelection(e: ISelectionEvent): void { - if (e.payload && !e.payload.didClickElement) { + if (e.payload && e.payload.didClickOnTwistie) { return; } let [first] = e.selection; From b6a8d0c7614a3854cc85d9e694287b3758d9a185 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 11:32:40 +0200 Subject: [PATCH 042/151] split outline styles into outline tree/panel styles, style context views --- .../documentSymbols/media/outlineTree.css | 66 +++++++++++++++++++ .../contrib/documentSymbols/outlineTree.ts | 7 +- .../browser/parts/editor/editorBreadcrumbs.ts | 2 +- .../outline/electron-browser/outlinePanel.css | 53 --------------- 4 files changed, 71 insertions(+), 57 deletions(-) create mode 100644 src/vs/editor/contrib/documentSymbols/media/outlineTree.css diff --git a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css new file mode 100644 index 00000000000..f62841df77d --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-tree.focused .selected .outline-element-label, .monaco-tree.focused .selected .outline-element-decoration { + /* make sure selection color wins when a label is being selected */ + color: inherit !important; +} + +.monaco-tree .outline-element { + display: flex; + flex: 1; + flex-flow: row nowrap; + align-items: center; +} + +.monaco-tree .outline-element .outline-element-icon { + padding-right: 3px; +} + +/* .monaco-tree.no-icons .outline-element .outline-element-icon { + display: none; +} */ + +.monaco-tree .outline-element .outline-element-label { + text-overflow: ellipsis; + overflow: hidden; + color: var(--outline-element-color); +} + +.monaco-tree .outline-element .outline-element-label .monaco-highlighted-label .highlight { + font-weight: bold; +} + +.monaco-tree .outline-element .outline-element-detail { + visibility: hidden; + flex: 1; + flex-basis: 10%; + opacity: 0.8; + overflow: hidden; + text-overflow: ellipsis; + font-size: 90%; + padding-left: 4px; + padding-top: 3px; +} + +.monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail { + visibility: inherit; +} + +.monaco-tree .outline-element .outline-element-decoration { + opacity: 0.75; + font-size: 90%; + font-weight: 600; + padding: 0 12px 0 5px; + margin-left: auto; + text-align: center; + color: var(--outline-element-color); +} + +.monaco-tree .outline-element .outline-element-decoration.bubble { + font-family: octicons; + font-size: 14px; + opacity: 0.4; +} diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 19489cbae98..e1181ea33cd 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -5,22 +5,23 @@ 'use strict'; import * as dom from 'vs/base/browser/dom'; -import 'vs/css!./media/symbol-icons'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { values } from 'vs/base/common/collections'; import { createMatches } from 'vs/base/common/filters'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; +import 'vs/css!./media/outlineTree'; +import 'vs/css!./media/symbol-icons'; import { Range } from 'vs/editor/common/core/range'; -import { symbolKindToCssClass, SymbolKind } from 'vs/editor/common/modes'; +import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export enum OutlineItemCompareType { ByPosition, diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index c824ecef19d..314b6848ead 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -201,7 +201,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { return event.node; }, render: (container: HTMLElement) => { - dom.addClasses(container, 'show-file-icons'); + dom.addClasses(container, 'monaco-workbench', 'show-file-icons'); let { element } = event.item as Item; let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; let res = this._instantiationService.createInstance(ctor, container, element); diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css index 036064d3c6a..858ba8e3266 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css @@ -76,59 +76,6 @@ color: inherit !important; } -.monaco-workbench .outline-panel .outline-element { - display: flex; - flex: 1; - flex-flow: row nowrap; - align-items: center; -} - -.monaco-workbench .outline-panel .outline-element .outline-element-icon { - padding-right: 3px; -} - .monaco-workbench .outline-panel.no-icons .outline-element .outline-element-icon { display: none; } - -.monaco-workbench .outline-panel .outline-element .outline-element-label { - text-overflow: ellipsis; - overflow: hidden; - color: var(--outline-element-color); -} - -.monaco-workbench .outline-panel .outline-element .outline-element-label .monaco-highlighted-label .highlight { - font-weight: bold; -} - -.monaco-workbench .outline-panel .outline-element .outline-element-detail { - visibility: hidden; - flex: 1; - flex-basis: 10%; - opacity: 0.8; - overflow: hidden; - text-overflow: ellipsis; - font-size: 90%; - padding-left: 4px; - padding-top: 3px; -} - -.monaco-workbench .outline-panel .monaco-tree-row.focused .outline-element .outline-element-detail { - visibility: inherit; -} - -.monaco-workbench .outline-panel .outline-element .outline-element-decoration { - opacity: 0.75; - font-size: 90%; - font-weight: 600; - padding: 0 12px 0 5px; - margin-left: auto; - text-align: center; - color: var(--outline-element-color); -} - -.monaco-workbench .outline-panel .outline-element .outline-element-decoration.bubble { - font-family: octicons; - font-size: 14px; - opacity: 0.4; -} From d506fa59ada07fd4bf77f08340c9427719e845ad Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 12:05:11 +0200 Subject: [PATCH 043/151] add setting to enable/disable breadcrumbs --- .../browser/parts/editor/editorBreadcrumbs.ts | 82 +++++++++++++++++-- .../browser/parts/editor/editorGroupView.ts | 7 ++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 314b6848ead..f0f9bd00fe0 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -12,7 +12,7 @@ import { compareFileNames } from 'vs/base/common/comparers'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { isEqual, dirname } from 'vs/base/common/resources'; +import { dirname, isEqual } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; @@ -21,22 +21,26 @@ import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IConstructorSignature2, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; +import { Registry } from 'vs/platform/registry/common/platform'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; +import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { EditorInput } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorBreadcrumbs, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; - +import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; class Item extends BreadcrumbsItem { @@ -99,6 +103,8 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { private readonly _ckBreadcrumbsVisible: IContextKey; private readonly _ckBreadcrumbsFocused: IContextKey; + private readonly _cfEnabled: Config; + private readonly _disposables = new Array(); private readonly _domNode: HTMLDivElement; private readonly _widget: BreadcrumbsWidget; @@ -107,7 +113,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { constructor( container: HTMLElement, - private readonly _editorGroup: IEditorGroup, + private readonly _editorGroup: EditorGroupView, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IContextViewService private readonly _contextViewService: IContextViewService, @IEditorService private readonly _editorService: IEditorService, @@ -115,6 +121,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IThemeService private readonly _themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, ) { this._domNode = document.createElement('div'); dom.addClasses(this._domNode, 'editor-breadcrumbs', 'show-file-icons'); @@ -125,6 +132,17 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._widget.onDidChangeFocus(val => this._ckBreadcrumbsFocused.set(val), undefined, this._disposables); this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService)); + this._cfEnabled = Config.create(configurationService, 'breadcrumbs.enabled'); + this._disposables.push(this._cfEnabled.onDidChange(value => { + if (!value) { + this.closeEditor(undefined); + this._editorGroup.relayout(); + } else if (this._editorGroup.activeEditor) { + this.openEditor(this._editorGroup.activeEditor); + this._editorGroup.relayout(); + } + })); + this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); this._ckBreadcrumbsFocused = EditorBreadcrumbs.CK_BreadcrumbsFocused.bindTo(this._contextKeyService); } @@ -133,10 +151,11 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { dispose(this._disposables); this._widget.dispose(); this._ckBreadcrumbsVisible.reset(); + this._cfEnabled.dispose(); } getPreferredHeight(): number { - return 25; + return this._cfEnabled.value ? 25 : 0; } layout(dim: dom.Dimension): void { @@ -150,6 +169,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } openEditor(input: EditorInput): void { + if (!this._cfEnabled.value) { + // not enabled -> return early + return; + } this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables); @@ -413,6 +436,55 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } } +//#region config + +abstract class Config { + + name: string; + value: T; + onDidChange: Event; + abstract dispose(): void; + + static create(service: IConfigurationService, name: string): Config { + + let value: T = service.getValue(name); + let onDidChange = new Emitter(); + + let listener = service.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(name)) { + value = service.getValue(name); + onDidChange.fire(value); + } + }); + + return { + name, + get value() { return value; }, + onDidChange: onDidChange.event, + dispose(): void { + listener.dispose(); + onDidChange.dispose(); + } + }; + } +} + +Registry.as(Extensions.Configuration).registerConfiguration({ + id: 'breadcrumbs', + title: localize('title', "Breadcrumb Navigation"), + order: 101, + type: 'object', + properties: { + 'breadcrumbs.enabled': { + 'description': localize('enabled', "Enable/disable navigation breadcrumbss"), + 'type': 'boolean', + 'default': false + } + } +}); + +//#endregion + //#region commands KeybindingsRegistry.registerCommandAndKeybindingRule({ diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 39385de58cb..a31d41c5797 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1380,6 +1380,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + this.breadcrumbsControl.getPreferredHeight()))); } + relayout(): void { + if (this.dimension) { + const { width, height } = this.dimension; + this.layout(width, height); + } + } + toJSON(): ISerializedEditorGroup { return this._group.serialize(); } From f7d447ffa1e3ac6b778719f111412e769c1fbe14 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 12:13:04 +0200 Subject: [PATCH 044/151] don't access disposed buffer --- .../browser/parts/editor/editorBreadcrumbsModel.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts index 913175259ac..87031bf8046 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -78,11 +78,15 @@ export class EditorBreadcrumbsModel { if (!this._editor) { return; } - this._updateOutline(); + // update as model changes this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline())); this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true))); + this._updateOutline(); + + // stop when editor dies + this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables))); } private _updateOutline(didChangeContent?: boolean): void { @@ -111,7 +115,7 @@ export class EditorBreadcrumbsModel { const lastVersionId = buffer.getVersionId(); this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { - if (lastVersionId === buffer.getVersionId()) { + if (!buffer.isDisposed() && lastVersionId === buffer.getVersionId()) { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); } }, 150); From a7f7ebf2fa9150f1977ca39b29528322bc54846f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 12:13:41 +0200 Subject: [PATCH 045/151] :lipstick: --- .../workbench/browser/parts/editor/editorBreadcrumbsModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts index 87031bf8046..2c1bf381f4d 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -148,13 +148,13 @@ export class EditorBreadcrumbsModel { } private _updateOutlineElements(elements: (OutlineGroup | OutlineElement)[]): void { - if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel.outlineElementEquals)) { + if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel._outlineElementEquals)) { this._outlineElements = elements; this._onDidUpdate.fire(this); } } - private static outlineElementEquals(a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): boolean { + private static _outlineElementEquals(a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): boolean { if (a === b) { return true; } else if (!a || !b) { From 5b4261c3afd8615e734f04f74a3e31df2bd832a6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 14:39:44 +0200 Subject: [PATCH 046/151] better selection/focus management in breadcrumbs --- .../ui/breadcrumbs/breadcrumbsWidget.css | 5 +- .../ui/breadcrumbs/breadcrumbsWidget.ts | 55 +++++++++++-------- .../browser/parts/editor/editorBreadcrumbs.ts | 16 ++++-- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 08b25c9bc4e..a7f820dee40 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -33,5 +33,8 @@ .vs-dark .monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after { background-image: url(./collpased-dark.svg); - +} + +.monaco-breadcrumbs .monaco-breadcrumb-item.focused { + font-weight: bold; } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 0ed02f6e1b1..db98d5d18af 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -133,10 +133,18 @@ export class BreadcrumbsWidget { } } - focus(): void { + domFocus(): void { this._domNode.focus(); } + getFocused(): BreadcrumbsItem { + return this._items[this._focusedItemIdx]; + } + + setFocused(item: BreadcrumbsItem): void { + this._focus(this._items.indexOf(item)); + } + focusPrev(): any { this._focus((this._focusedItemIdx - 1 + this._nodes.length) % this._nodes.length); this._domNode.focus(); @@ -147,39 +155,39 @@ export class BreadcrumbsWidget { this._domNode.focus(); } - private _focus(nth: number): boolean { - if (this._focusedItemIdx >= 0 && this._focusedItemIdx < this._nodes.length) { - dom.removeClass(this._nodes[this._focusedItemIdx], 'focused'); - this._focusedItemIdx = -1; + private _focus(nth: number): void { + this._focusedItemIdx = -1; + for (let i = 0; i < this._nodes.length; i++) { + const node = this._nodes[i]; + if (i !== nth) { + dom.removeClass(node, 'focused'); + } else { + this._focusedItemIdx = i; + dom.addClass(node, 'focused'); + } } - if (nth < 0 || nth >= this._nodes.length) { - return false; - } - this._focusedItemIdx = nth; - dom.addClass(this._nodes[this._focusedItemIdx], 'focused'); - this._scrollable.setScrollPosition({ scrollLeft: this._nodes[this._focusedItemIdx].offsetLeft }); this._onDidFocusItem.fire({ item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx] }); - return true; } - getFocusedItem(): BreadcrumbsItem { - return this._items[this._focusedItemIdx]; + getSelected(): BreadcrumbsItem { + return this._items[this._selectedItemIdx]; } - select(item: BreadcrumbsItem): void { + setSelected(item: BreadcrumbsItem): void { this._select(this._items.indexOf(item)); } private _select(nth: number): void { - if (this._selectedItemIdx >= 0 && this._selectedItemIdx < this._nodes.length) { - dom.removeClass(this._nodes[this._selectedItemIdx], 'selected'); - this._selectedItemIdx = -1; + this._selectedItemIdx = -1; + for (let i = 0; i < this._nodes.length; i++) { + const node = this._nodes[i]; + if (i !== nth) { + dom.removeClass(node, 'selected'); + } else { + this._selectedItemIdx = i; + dom.addClass(node, 'selected'); + } } - if (nth < 0 || nth >= this._nodes.length) { - return; - } - this._selectedItemIdx = nth; - dom.addClass(this._nodes[this._selectedItemIdx], 'selected'); this._onDidSelectItem.fire({ item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx] }); } @@ -212,7 +220,6 @@ export class BreadcrumbsWidget { this._nodes[start] = node; } this.layout(undefined); - this._focus(this._nodes.length - 1); } private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void { diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index f0f9bd00fe0..065dc3c76bf 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -198,7 +198,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } focus(): void { - this._widget.focus(); + this._widget.domFocus(); } focusNext(): void { @@ -210,15 +210,18 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } select(): void { - const item = this._widget.getFocusedItem(); + const item = this._widget.getFocused(); if (item) { - this._widget.select(item); + this._widget.setSelected(item); } } private _onDidSelectItem(event: IBreadcrumbsItemEvent): void { - this._editorGroup.focus(); + if (!event.item) { + return; + } + this._editorGroup.focus(); this._contextViewService.showContextView({ getAnchor() { return event.node; @@ -231,7 +234,6 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { res.layout({ width: 250, height: 300 }); res.onDidPickElement(data => { this._contextViewService.hideContextView(); - this._widget.select(undefined); if (!data) { return; } @@ -257,6 +259,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { }); return res; }, + onHide: () => { + this._widget.setSelected(undefined); + this._widget.setFocused(undefined); + } }); } } From 9b876381cbfd20b11ab8eb6a11e6aaccd24b8549 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 9 Jul 2018 06:10:57 -0700 Subject: [PATCH 047/151] Use m for Terminal menu mnemonic Fixes #53828 --- src/vs/code/electron-main/menus.ts | 2 +- src/vs/workbench/browser/parts/menubar/menubarPart.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index cbcf41f5afa..1cc94b2b298 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -241,7 +241,7 @@ export class CodeMenu { // Terminal const terminalMenu = new Menu(); - const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal")), submenu: terminalMenu }); + const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal")), submenu: terminalMenu }); this.setTerminalMenu(terminalMenu); // Debug diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index 31c84306e65..208b9170360 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -74,7 +74,7 @@ export class MenubarPart extends Part { 'Selection': nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection"), 'View': nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View"), 'Go': nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go"), - 'Terminal': nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), + 'Terminal': nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal"), 'Debug': nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug"), 'Tasks': nls.localize({ key: 'mTasks', comment: ['&& denotes a mnemonic'] }, "&&Tasks"), 'Help': nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help") From e89b02b448aa17353d2b58da83bc5a3e33c593cc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Jul 2018 15:10:15 +0200 Subject: [PATCH 048/151] Fix #53872 --- .../sharedProcess/sharedProcessMain.ts | 3 ++- .../common/extensionManagementIpc.ts | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 97f712452c9..deaea3955f3 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -43,6 +43,7 @@ import { LocalizationsChannel } from 'vs/platform/localizations/common/localizat import { DialogChannelClient } from 'vs/platform/dialogs/common/dialogIpc'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DefaultURITransformer } from 'vs/base/common/uriIpc'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -134,7 +135,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I instantiationService2.invokeFunction(accessor => { const extensionManagementService = accessor.get(IExtensionManagementService); - const channel = new ExtensionManagementChannel(extensionManagementService); + const channel = new ExtensionManagementChannel(extensionManagementService, DefaultURITransformer); server.registerChannel('extensions', channel); // clean up deprecated extensions diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 1de227bfe06..0aa4e7577a7 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -33,7 +33,7 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel { onUninstallExtension: Event; onDidUninstallExtension: Event; - constructor(private service: IExtensionManagementService) { + constructor(private service: IExtensionManagementService, private uriTransformer: IURITransformer) { this.onInstallExtension = buffer(service.onInstallExtension, true); this.onDidInstallExtension = buffer(service.onDidInstallExtension, true); this.onUninstallExtension = buffer(service.onUninstallExtension, true); @@ -55,15 +55,19 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel { switch (command) { case 'install': return this.service.install(args[0]); case 'installFromGallery': return this.service.installFromGallery(args[0]); - case 'uninstall': return this.service.uninstall(args[0], args[1]); - case 'reinstallFromGallery': return this.service.reinstallFromGallery(args[0]); + case 'uninstall': return this.service.uninstall(this._transform(args[0]), args[1]); + case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transform(args[0])); case 'getInstalled': return this.service.getInstalled(args[0]); - case 'updateMetadata': return this.service.updateMetadata(args[0], args[1]); + case 'updateMetadata': return this.service.updateMetadata(this._transform(args[0]), args[1]); case 'getExtensionsReport': return this.service.getExtensionsReport(); } throw new Error('Invalid call'); } + + private _transform(extension: ILocalExtension): ILocalExtension { + return extension ? { ...extension, ...{ location: URI.revive(this.uriTransformer.transformIncoming(extension.location)) } } : extension; + } } export class ExtensionManagementChannelClient implements IExtensionManagementService { From a335286070faab8add9489e1d35ebb82751b303a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 15:51:55 +0200 Subject: [PATCH 049/151] keep focused item, reset focused when item has changed --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts | 8 +++++++- .../workbench/browser/parts/editor/editorBreadcrumbs.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index db98d5d18af..29cee352905 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -13,7 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Event, Emitter } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; -import { commonPrefixLength } from 'vs/base/common/arrays'; +import { commonPrefixLength, tail } from 'vs/base/common/arrays'; export abstract class BreadcrumbsItem { dispose(): void { } @@ -134,6 +134,8 @@ export class BreadcrumbsWidget { } domFocus(): void { + const focused = this.getFocused() || tail(this._items); + this.setFocused(focused); this._domNode.focus(); } @@ -196,6 +198,9 @@ export class BreadcrumbsWidget { let removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); this._render(prefix); dispose(removed); + if (prefix >= this._focusedItemIdx) { + this._focus(-1); + } } private _render(start: number): void { @@ -224,6 +229,7 @@ export class BreadcrumbsWidget { private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void { dom.clearNode(container); + container.className = ''; item.render(container); dom.append(container); dom.addClass(container, 'monaco-breadcrumb-item'); diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index 065dc3c76bf..d88e3047feb 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -261,7 +261,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { }, onHide: () => { this._widget.setSelected(undefined); - this._widget.setFocused(undefined); + // this._widget.setFocused(undefined); } }); } From c209a9e149aa5a2762b6e1b54a149db223020b3d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 15:53:24 +0200 Subject: [PATCH 050/151] fix #53836 --- .../workbench/parts/outline/electron-browser/outlinePanel.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 3650e6cf8cb..66cbf82e5ae 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -352,6 +352,9 @@ export class OutlinePanel extends ViewletPanel { return false; } const keyInfo = mapping[event.code]; + if (!keyInfo) { + return false; + } if (keyInfo.value) { $this._input.focus(); return true; From 31bdf0d1c69de38413999fd9ef6d25de99394277 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 9 Jul 2018 16:22:14 +0200 Subject: [PATCH 051/151] remove ppromise usage from task --- src/vs/base/node/processes.ts | 10 ++--- .../parts/tasks/node/processRunnerDetector.ts | 27 +++++++------ .../parts/tasks/node/processTaskSystem.ts | 40 ++++++++++--------- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index eb01c197f74..ebe0dff4cc4 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import * as cp from 'child_process'; import { fork } from 'vs/base/node/stdFork'; import * as nls from 'vs/nls'; -import { PPromise, TPromise, TValueCallback, TProgressCallback, ErrorCallback } from 'vs/base/common/winjs.base'; +import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import { IStringDictionary } from 'vs/base/common/collections'; import URI from 'vs/base/common/uri'; @@ -19,6 +19,8 @@ import { LineDecoder } from 'vs/base/node/decoder'; import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode, Executable } from 'vs/base/common/processes'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; +export type TProgressCallback = (progress: T) => void; + export interface LineData { line: string; source: Source; @@ -152,18 +154,16 @@ export abstract class AbstractProcess { return 'other'; } - public start(): PPromise { + public start(pp: TProgressCallback): TPromise { if (Platform.isWindows && ((this.options && this.options.cwd && TPath.isUNC(this.options.cwd)) || !this.options && !this.options.cwd && TPath.isUNC(process.cwd()))) { return TPromise.wrapError(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.'))); } return this.useExec().then((useExec) => { let cc: TValueCallback; let ee: ErrorCallback; - let pp: TProgressCallback; - let result = new PPromise((c, e, p) => { + let result = new TPromise((c, e) => { cc = c; ee = e; - pp = p; }); if (useExec) { diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index 8169d1f3c8c..8cec848aedb 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -12,7 +12,7 @@ import * as Strings from 'vs/base/common/strings'; import * as Collections from 'vs/base/common/collections'; import { CommandOptions, Source, ErrorData } from 'vs/base/common/processes'; -import { LineProcess } from 'vs/base/node/processes'; +import { LineProcess, LineData } from 'vs/base/node/processes'; import { IFileService } from 'vs/platform/files/common/files'; @@ -269,7 +269,20 @@ export class ProcessRunnerDetector { private runDetection(process: LineProcess, command: string, isShellCommand: boolean, matcher: TaskDetectorMatcher, problemMatchers: string[], list: boolean): TPromise { let tasks: string[] = []; matcher.init(); - return process.start().then((success) => { + + const onProgress = (progress: LineData) => { + if (progress.source === Source.stderr) { + this._stderr.push(progress.line); + return; + } + let line = Strings.removeAnsiEscapeCodes(progress.line); + let matches = matcher.match(tasks, line); + if (matches && matches.length > 0) { + tasks.push(matches[1]); + } + }; + + return process.start(onProgress).then((success) => { if (tasks.length === 0) { if (success.cmdCode !== 0) { if (command === 'gulp') { @@ -305,16 +318,6 @@ export class ProcessRunnerDetector { this._stderr.push(nls.localize('TaskSystemDetector.noProgram', 'Program {0} was not found. Message is {1}', command, error.message)); } return { config: null, stdout: this._stdout, stderr: this._stderr }; - }, (progress) => { - if (progress.source === Source.stderr) { - this._stderr.push(progress.line); - return; - } - let line = Strings.removeAnsiEscapeCodes(progress.line); - let matches = matcher.match(tasks, line); - if (matches && matches.length > 0) { - tasks.push(matches[1]); - } }); } diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index 8ec0759e9e1..810cfd8615b 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -249,7 +249,21 @@ export class ProcessTaskSystem implements ITaskSystem { this.activeTask = task; const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task); let processStartedSignaled: boolean = false; - const startPromise = this.childProcess.start(); + const onProgress = (progress: LineData) => { + let line = Strings.removeAnsiEscapeCodes(progress.line); + this.outputChannel.append(line + '\n'); + watchingProblemMatcher.processLine(line); + if (delayer === null) { + delayer = new Async.Delayer(3000); + } + delayer.trigger(() => { + watchingProblemMatcher.forceDelivery(); + return null; + }).then(() => { + delayer = null; + }); + }; + const startPromise = this.childProcess.start(onProgress); this.childProcess.pid.then(pid => { if (pid !== -1) { processStartedSignaled = true; @@ -287,19 +301,6 @@ export class ProcessTaskSystem implements ITaskSystem { } eventCounter = 0; return this.handleError(task, error); - }, (progress: LineData) => { - let line = Strings.removeAnsiEscapeCodes(progress.line); - this.outputChannel.append(line + '\n'); - watchingProblemMatcher.processLine(line); - if (delayer === null) { - delayer = new Async.Delayer(3000); - } - delayer.trigger(() => { - watchingProblemMatcher.forceDelivery(); - return null; - }).then(() => { - delayer = null; - }); }); let result: ITaskExecuteResult = (task).tscWatch ? { kind: TaskExecuteKind.Started, started: { restartOnFileChanges: '**/*.ts' }, promise: this.activeTaskPromise } @@ -312,7 +313,12 @@ export class ProcessTaskSystem implements ITaskSystem { this.activeTask = task; const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task); let processStartedSignaled: boolean = false; - const startPromise = this.childProcess.start(); + const onProgress = (progress) => { + let line = Strings.removeAnsiEscapeCodes(progress.line); + this.outputChannel.append(line + '\n'); + startStopProblemMatcher.processLine(line); + }; + const startPromise = this.childProcess.start(onProgress); this.childProcess.pid.then(pid => { if (pid !== -1) { processStartedSignaled = true; @@ -340,10 +346,6 @@ export class ProcessTaskSystem implements ITaskSystem { this._onDidStateChange.fire(inactiveEvent); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); return this.handleError(task, error); - }, (progress) => { - let line = Strings.removeAnsiEscapeCodes(progress.line); - this.outputChannel.append(line + '\n'); - startStopProblemMatcher.processLine(line); }); return { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise }; } From dd68c6520b67d097453dc2766f976dc009a5af44 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 16:25:20 +0200 Subject: [PATCH 052/151] don't access disposed model --- .../browser/parts/editor/editorBreadcrumbsModel.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts index 2c1bf381f4d..bb1e22f0e68 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -102,25 +102,26 @@ export class EditorBreadcrumbsModel { } const source = new CancellationTokenSource(); + const versionIdThen = buffer.getVersionId(); + const timeout = new TimeoutTimer(); this._outlineDisposables.push({ dispose: () => { source.cancel(); source.dispose(); + timeout.dispose(); } }); + OutlineModel.create(buffer, source.token).then(model => { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); - const timeout = new TimeoutTimer(); - const lastVersionId = buffer.getVersionId(); this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { - if (!buffer.isDisposed() && lastVersionId === buffer.getVersionId()) { + if (versionIdThen === buffer.getVersionId()) { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); } }, 150); })); - this._outlineDisposables.push(timeout); }).catch(err => { this._updateOutlineElements([]); onUnexpectedError(err); From 7059b50648c8909befceabb20da8a59fa34de815 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 9 Jul 2018 16:54:28 +0200 Subject: [PATCH 053/151] node-debug@1.26.1 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index bf739296d29..ab3a77f657b 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.26.0", + "version": "1.26.1", "repo": "https://github.com/Microsoft/vscode-node-debug" }, { From 25cabacf5d1111539c4e4f587855cf4e2d65cfec Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 9 Jul 2018 17:00:24 +0200 Subject: [PATCH 054/151] add log statements to catch weird bug --- src/vs/base/browser/ui/list/listView.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 1d560a8afec..bbfe7f0b134 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -282,6 +282,11 @@ export class ListView implements ISpliceable, IDisposable { private insertItemInDOM(index: number, beforeElement: HTMLElement | null): void { const item = this.items[index]; + if (!item) { + console.log(this.items); + throw new Error(`Got index ${index} and there are ${this.items.length} items. File issue to joao!`); + } + if (!item.row) { item.row = this.cache.alloc(item.templateId); } @@ -311,6 +316,12 @@ export class ListView implements ISpliceable, IDisposable { private removeItemFromDOM(index: number): void { const item = this.items[index]; + + if (!item) { + console.log(this.items); + throw new Error(`Got index ${index} and there are ${this.items.length} items. File issue to joao!`); + } + this.cache.release(item.row); item.row = null; } @@ -371,7 +382,12 @@ export class ListView implements ISpliceable, IDisposable { } private onScroll(e: ScrollEvent): void { - this.render(e.scrollTop, e.height); + try { + this.render(e.scrollTop, e.height); + } catch (err) { + console.log('Got bad scroll event:', e); + throw err; + } } private onTouchChange(event: GestureEvent): void { From 8422fccff1f6256f4bbf272b055de3ec7652f067 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Jul 2018 17:25:59 +0200 Subject: [PATCH 055/151] add shadow/border to breadcrumb picker --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 6 ++++-- .../browser/parts/editor/media/editorbreadcrumbs.css | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index d88e3047feb..d271ac2a475 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -227,7 +227,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { return event.node; }, render: (container: HTMLElement) => { - dom.addClasses(container, 'monaco-workbench', 'show-file-icons'); + dom.addClasses(container, 'monaco-breadcrumbs-picker', 'monaco-workbench', 'show-file-icons'); + let color = this._themeService.getTheme().getColor(SIDE_BAR_BACKGROUND); + container.style.borderColor = color.darken(.2).toString(); + container.style.boxShadow = `${color.toString()} 6px 6px 6px -6px;`; let { element } = event.item as Item; let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; let res = this._instantiationService.createInstance(ctor, container, element); @@ -294,7 +297,6 @@ export abstract class BreadcrumbsPicker { this.focus = dom.trackFocus(this._domNode); this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); - this._tree.domFocus(); this._tree.setInput(this._getInput(input)); } diff --git a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css index e3b45ae4166..e40ca9ba696 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css +++ b/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css @@ -11,3 +11,8 @@ .monaco-workbench>.part.editor>.content .editor-group-container .editor-breadcrumbs .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ padding-left: 8px; } + +.monaco-breadcrumbs-picker { + border: 1px #dddddd solid; + box-shadow: #dddddd 6px 6px 6px -6px; +} From b6902fd6629207527a0aae776cef7e95d1178f3b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 9 Jul 2018 09:21:05 -0700 Subject: [PATCH 056/151] Fix #53838 - workaround strange error from telemetry --- extensions/search-rg/src/ripgrepFileSearch.ts | 14 ++++++++++---- .../workbench/services/search/node/fileSearch.ts | 13 +++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/extensions/search-rg/src/ripgrepFileSearch.ts b/extensions/search-rg/src/ripgrepFileSearch.ts index 7edf4c4f4b9..fd3b6acc99a 100644 --- a/extensions/search-rg/src/ripgrepFileSearch.ts +++ b/extensions/search-rg/src/ripgrepFileSearch.ts @@ -114,16 +114,22 @@ export class RipgrepFileSearchEngine implements IInternalFileSearchProvider { cb(err, stdout, last); }; + let gotData = false; if (cmd.stdout) { + // Should be non-null, but #38195 this.forwardData(cmd.stdout, onData); + cmd.stdout.once('data', () => gotData = true); } else { this.outputChannel.appendLine('stdout is null'); } - const stderr = this.collectData(cmd.stderr); - - let gotData = false; - cmd.stdout.once('data', () => gotData = true); + let stderr: Buffer[]; + if (cmd.stderr) { + // Should be non-null, but #38195 + stderr = this.collectData(cmd.stderr); + } else { + this.outputChannel.appendLine('stderr is null'); + } cmd.on('error', (err: Error) => { onData(err); diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index dcfb3c3db8e..1d6dc527cae 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -384,17 +384,22 @@ export class FileWalker { cb(err, stdout, last); }; + let gotData = false; if (cmd.stdout) { // Should be non-null, but #38195 this.forwardData(cmd.stdout, encoding, onData); + cmd.stdout.once('data', () => gotData = true); } else { onMessage({ message: 'stdout is null' }); } - const stderr = this.collectData(cmd.stderr); - - let gotData = false; - cmd.stdout.once('data', () => gotData = true); + let stderr: Buffer[]; + if (cmd.stderr) { + // Should be non-null, but #38195 + stderr = this.collectData(cmd.stderr); + } else { + onMessage({ message: 'stderr is null' }); + } cmd.on('error', (err: Error) => { onData(err); From 3ea1e53bbd384c09020b505611255ba5fbd06e2a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 6 Jul 2018 15:23:52 -0700 Subject: [PATCH 057/151] Update to TS 3.0 insiders --- .../server/src/modes/javascriptMode.ts | 2 +- extensions/package.json | 2 +- .../src/features/baseCodeLensProvider.ts | 2 +- .../src/features/updatePathsOnRename.ts | 2 +- .../src/typeScriptServiceClientHost.ts | 3 +-- .../src/typescriptService.ts | 5 ----- .../src/utils/previewer.ts | 17 +++++++++++------ extensions/yarn.lock | 6 +++--- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 191253d0d2c..0c124f2d468 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -131,7 +131,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { let span = info.span; diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 67be9e314c0..729bda6af6c 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -11,11 +11,6 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; import BufferSyncSupport from './features/bufferSyncSupport'; -declare module './protocol' { - export type JsxClosingTagRequestArgs = any; - export type JsxClosingTagResponse = any; -} - export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). diff --git a/extensions/typescript-language-features/src/utils/previewer.ts b/extensions/typescript-language-features/src/utils/previewer.ts index ce10fcbbe04..41ff1fea7d6 100644 --- a/extensions/typescript-language-features/src/utils/previewer.ts +++ b/extensions/typescript-language-features/src/utils/previewer.ts @@ -72,13 +72,18 @@ export function markdownDocumentation( export function addMarkdownDocumentation( out: MarkdownString, - documentation: Proto.SymbolDisplayPart[], - tags: Proto.JSDocTagInfo[] + documentation: Proto.SymbolDisplayPart[] | undefined, + tags: Proto.JSDocTagInfo[] | undefined ): MarkdownString { - out.appendMarkdown(plain(documentation)); - const tagsPreview = tagsMarkdownPreview(tags); - if (tagsPreview) { - out.appendMarkdown('\n\n' + tagsPreview); + if (documentation) { + out.appendMarkdown(plain(documentation)); + } + + if (tags) { + const tagsPreview = tagsMarkdownPreview(tags); + if (tagsPreview) { + out.appendMarkdown('\n\n' + tagsPreview); + } } return out; } diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 7ec0734b094..64d1bf83d0f 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,6 +2,6 @@ # yarn lockfile v1 -typescript@2.9.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" +typescript@3.0.0-insiders.20180706: + version "3.0.0-insiders.20180706" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.0-insiders.20180706.tgz#684c6c8ca1eefb11e79b6c072a92046ffd75184e" From 3aa4b67b5f3e757a900ac168632a92e8afa23e2c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Jul 2018 09:24:04 -0700 Subject: [PATCH 058/151] Fix npe when actions are not provided Fixes #53831 --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index c86a8b15608..80d248a2819 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -88,7 +88,7 @@ export class QuickFixController implements IEditorContribution { this._activeRequest = e.actions; } - if (e && e.trigger.filter && e.trigger.filter.kind) { + if (e && e.actions && e.trigger.filter && e.trigger.filter.kind) { // Triggered for specific scope // Apply if we only have one action or requested autoApply, otherwise show menu e.actions.then(fixes => { From bda7b4480a9eac88931f0068b69702881cf39a51 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Jul 2018 09:34:03 -0700 Subject: [PATCH 059/151] Update markdown grammar Fixes https://github.com/Microsoft/vscode/issues/53860 --- extensions/markdown-basics/syntaxes/markdown.tmLanguage.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 2838349491e..d49d0688467 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/fc14af6c0ca3c2bdaa1681f6f51834fe6a0a4279", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4504240cdb13a4640f64fc98a0adb858226a879e", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -722,7 +722,7 @@ "contentName": "meta.embedded.block.dosbatch", "patterns": [ { - "include": "source.dosbatch" + "include": "source.batchfile" } ] } From 0e68a1a14acf64352d93a23007442f212864727e Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 9 Jul 2018 10:11:02 -0700 Subject: [PATCH 060/151] Changes for feedback --- .../browser/parts/views/media/panelviewlet.css | 2 +- .../browser/parts/views/panelViewlet.ts | 6 +++--- .../debug/electron-browser/callStackView.ts | 9 ++++----- .../electron-browser/extensionsViews.ts | 6 ++---- .../electron-browser/views/openEditorsView.ts | 5 ++--- .../scm/electron-browser/media/scmViewlet.css | 2 +- .../parts/scm/electron-browser/scmViewlet.ts | 18 +++++++++--------- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/panelviewlet.css index 1dadcbfc4dd..7d6c4e36d60 100644 --- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/panelviewlet.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-panel-view .panel > .panel-header > h3.title { +.monaco-panel-view .panel > .panel-header h3.title { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 86886c0ea7e..a647bf39021 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -100,7 +100,7 @@ export abstract class ViewletPanel extends Panel implements IView { protected renderHeader(container: HTMLElement): void { this.headerContainer = container; - this.renderHeaderTitle(container, [], [this.title]); + this.renderHeaderTitle(container, this.title); const actions = append(container, $('.actions')); this.toolbar = new ToolBar(actions, this.contextMenuService, { @@ -119,8 +119,8 @@ export abstract class ViewletPanel extends Panel implements IView { this.updateActionsVisibility(); } - protected renderHeaderTitle(container: HTMLElement, classList: string[], children: (string | Node)[]): void { - append(container, $('h3.' + ['title', ...classList].join('.'), null, ...children)); + protected renderHeaderTitle(container: HTMLElement, title: string): void { + append(container, $('h3.title', null, title)); } focus(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index cd6b2300ab5..3868462dbe7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -88,13 +88,12 @@ export class CallStackView extends TreeViewsViewletPanel { } protected renderHeaderTitle(container: HTMLElement): void { - const name = $('span'); - name.textContent = this.options.title; - this.pauseMessage = $('span.pause-message'); + let titleContainer = dom.append(container, $('.debug-call-stack-title')); + super.renderHeaderTitle(titleContainer, this.options.title); + + this.pauseMessage = dom.append(titleContainer, $('span.pause-message')); this.pauseMessage.hidden = true; this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); - - super.renderHeaderTitle(container, ['debug-call-stack-title'], [name, this.pauseMessage]); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 5075965732d..80e64760492 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -72,13 +72,11 @@ export class ExtensionsListView extends ViewletPanel { } renderHeaderTitle(container: HTMLElement): void { - let title = $('span'); - title.textContent = this.options.title; + super.renderHeaderTitle(container, this.options.title); - this.badgeContainer = $('.count-badge-wrapper'); + this.badgeContainer = append(container, $('.count-badge-wrapper')); this.badge = new CountBadge(this.badgeContainer); this.disposables.push(attachBadgeStyler(this.badge, this.themeService)); - super.renderHeaderTitle(container, [], [title, this.badgeContainer]); } renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index ace6268d637..e1b08ea5801 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -176,9 +176,9 @@ export class OpenEditorsView extends ViewletPanel { } protected renderHeaderTitle(container: HTMLElement): void { + super.renderHeaderTitle(container, this.title); - const title = $('span', null, this.title); - const count = $('.count'); + const count = dom.append(container, $('.count')); this.dirtyCountElement = dom.append(count, $('.monaco-count-badge')); this.disposables.push((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { @@ -195,7 +195,6 @@ export class OpenEditorsView extends ViewletPanel { }))); this.updateDirtyIndicator(); - super.renderHeaderTitle(container, [], [title, count]); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index f6e3ec0c18a..6d43b759884 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -51,7 +51,7 @@ display: none; } -.scm-viewlet .scm-provider > .name > .type { +.scm-viewlet .scm-provider > .type { opacity: 0.7; margin-left: 0.5em; font-size: 0.9em; diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 61fb04bd5ff..7d3f0bae8dc 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -765,22 +765,22 @@ export class RepositoryPanel extends ViewletPanel { } protected renderHeaderTitle(container: HTMLElement): void { - const name = $('.name'); - const title = append(name, $('span.title')); - const type = append(name, $('span.type')); + let title: string; + let type: string; if (this.repository.provider.rootUri) { - title.textContent = basename(this.repository.provider.rootUri.fsPath); - type.textContent = this.repository.provider.label; + title = basename(this.repository.provider.rootUri.fsPath); + type = this.repository.provider.label; } else { - title.textContent = this.repository.provider.label; - type.textContent = ''; + title = this.repository.provider.label; + type = ''; } + super.renderHeaderTitle(container, title); + addClass(container, 'scm-provider'); + append(container, $('span.type', null, type)); const onContextMenu = mapEvent(stop(domEvent(container, 'contextmenu')), e => new StandardMouseEvent(e)); onContextMenu(this.onContextMenu, this, this.disposables); - - super.renderHeaderTitle(container, ['scm-provider'], [name]); } private onContextMenu(event: StandardMouseEvent): void { From 48b3aaf2a45e728bed04278bafc3d9b6745dabc5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 9 Jul 2018 10:19:18 -0700 Subject: [PATCH 061/151] Settings editor - fix offset scroll shadows --- .../parts/preferences/browser/media/settingsEditor2.css | 3 +-- src/vs/workbench/parts/preferences/browser/settingsTree.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index b3b54ddc0cc..8ac1386f601 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -116,7 +116,6 @@ .settings-editor > .settings-body .settings-toc-container { width: 175px; margin-right: 5px; - padding-top: 5px; } .settings-editor > .settings-body .settings-toc-container.hidden { @@ -296,7 +295,7 @@ } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-1.settings-group-first { - padding-top: 7px; + padding-top: 4px; } .settings-editor > .settings-body .settings-feedback-button { diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index ac53257f46e..95a40dc3071 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -457,7 +457,7 @@ export class SettingsRenderer implements IRenderer { getHeight(tree: ITree, element: SettingsTreeElement): number { if (element instanceof SettingsTreeGroupElement) { if (element.isFirstGroup) { - return 31; + return 28; } return 40 + (7 * element.level); From ba795060a4c997331c2062017ea27cf7f7faf6a5 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 9 Jul 2018 10:48:49 -0700 Subject: [PATCH 062/151] Allow toggle details on suggest widget when frozen. Fixes #53882 --- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 6e72075c97e..9361c2b2422 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -878,7 +878,7 @@ export class SuggestWidget implements IContentWidget, IDelegate */ this.telemetryService.publicLog('suggestWidget:collapseDetails', this.editor.getTelemetryData()); } else { - if (this.state !== State.Open && this.state !== State.Details) { + if (this.state !== State.Open && this.state !== State.Details && this.state !== State.Frozen) { return; } From 8e4b4b8f3dc6419083206e8c76e287640ded202c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 9 Jul 2018 10:53:31 -0700 Subject: [PATCH 063/151] Settings editor - change keybinding and actions to open new editor by default (for insiders) --- src/vs/code/electron-main/menus.ts | 2 +- .../workbench/browser/parts/menubar/menubar.contribution.ts | 2 +- .../preferences/electron-browser/preferences.contribution.ts | 5 +++-- src/vs/workbench/parts/update/electron-browser/update.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 1cc94b2b298..de96fe1c654 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -435,7 +435,7 @@ export class CodeMenu { } private getPreferencesMenu(): Electron.MenuItem { - const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings'); + const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings2'); const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings'); const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions'); const snippetsSettings = this.createMenuItem(nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), 'workbench.action.openSnippets'); diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index e1d7d69ccdd..3d651933593 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -1291,7 +1291,7 @@ function preferencesMenuRegistration() { MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { group: '1_settings', command: { - id: 'workbench.action.openSettings', + id: 'workbench.action.openSettings2', title: nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings") }, order: 1 diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index a38ea158de6..0d626c51ccf 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -35,6 +35,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { PreferencesSearchService } from 'vs/workbench/parts/preferences/electron-browser/preferencesSearch'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { Command } from 'vs/editor/browser/editorExtensions'; +import product from 'vs/platform/node/product'; registerSingleton(IPreferencesSearchService, PreferencesSearchService); @@ -191,8 +192,8 @@ Registry.as(EditorInputExtensions.EditorInputFactor const category = nls.localize('preferences', "Preferences"); const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRawDefaultSettingsAction, OpenRawDefaultSettingsAction.ID, OpenRawDefaultSettingsAction.LABEL), 'Preferences: Open Raw Default Settings', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettingsAction, OpenSettingsAction.ID, OpenSettingsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA }), 'Preferences: Open Settings', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL), 'Preferences: Open Settings (Preview)', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettingsAction, OpenSettingsAction.ID, OpenSettingsAction.LABEL), 'Preferences: Open Settings', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA }), 'Preferences: Open Settings (Preview)', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), 'Preferences: Open User Settings', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL, { primary: null }), 'Preferences: Open Keyboard Shortcuts File', category); diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index 110be30861b..17f4f5930e5 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -277,7 +277,7 @@ class CommandAction extends Action { export class UpdateContribution implements IGlobalActivity { private static readonly showCommandsId = 'workbench.action.showCommands'; - private static readonly openSettingsId = 'workbench.action.openSettings'; + private static readonly openSettingsId = 'workbench.action.openSettings2'; private static readonly openKeybindingsId = 'workbench.action.openGlobalKeybindings'; private static readonly openUserSnippets = 'workbench.action.openSnippets'; private static readonly selectColorThemeId = 'workbench.action.selectTheme'; From 2be6da193d5c5d426e4e051e33deff23ea1d391e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 9 Jul 2018 10:57:08 -0700 Subject: [PATCH 064/151] Fix unused import --- .../preferences/electron-browser/preferences.contribution.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index 0d626c51ccf..b0ea7b33c62 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -35,7 +35,6 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { PreferencesSearchService } from 'vs/workbench/parts/preferences/electron-browser/preferencesSearch'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { Command } from 'vs/editor/browser/editorExtensions'; -import product from 'vs/platform/node/product'; registerSingleton(IPreferencesSearchService, PreferencesSearchService); From 1fc1292f0c8f51686716d7e290645e99453c2128 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 9 Jul 2018 11:12:36 -0700 Subject: [PATCH 065/151] adding context change listener and buffering calls to setup the menu (#53622) * adding context change listener and buffering calls to setup the menu * addressing feedback --- .../browser/parts/menubar/menubarPart.ts | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index 208b9170360..e33dc07f208 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -34,6 +34,7 @@ import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, getWorkspaceLabel } from 'vs/platform/workspaces/common/workspaces'; import { getPathLabel } from 'vs/base/common/labels'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { RunOnceScheduler } from 'vs/base/common/async'; interface CustomMenu { title: string; @@ -88,9 +89,11 @@ export class MenubarPart extends Part { private customMenus: CustomMenu[]; + private menuUpdater: RunOnceScheduler; private actionRunner: IActionRunner; private container: Builder; private recentlyOpened: IRecentlyOpened; + private updatePending: boolean; private _modifierKeyStatus: IModifierKeyStatus; private _isFocused: boolean; private _onVisibilityChange: Emitter; @@ -135,6 +138,8 @@ export class MenubarPart extends Part { this.topLevelMenus['Window'] = this._register(this.menuService.createMenu(MenuId.MenubarWindowMenu, this.contextKeyService)); } + this.menuUpdater = this._register(new RunOnceScheduler(() => this.doSetupMenubar(), 0)); + this.actionRunner = this._register(new ActionRunner()); this._register(this.actionRunner.onDidBeforeRun(() => { if (this.focusedMenu && this.focusedMenu.holder) { @@ -148,7 +153,7 @@ export class MenubarPart extends Part { for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { this._register(this.topLevelMenus[topLevelMenuName].onDidChange(() => this.setupMenubar())); } - this.setupMenubar(); + this.doSetupMenubar(); } this.isFocused = false; @@ -212,6 +217,15 @@ export class MenubarPart extends Part { } private set isFocused(value: boolean) { + if (this._isFocused && !value) { + // Losing focus, update the menu if needed + + if (this.updatePending) { + this.menuUpdater.schedule(); + this.updatePending = false; + } + } + this._isFocused = value; if (!this._isFocused && this.currentMenubarVisibility === 'toggle') { @@ -282,6 +296,9 @@ export class MenubarPart extends Part { // Listen to update service // this.updateService.onStateChange(() => this.setupMenubar()); + // Listen for context changes + this._register(this.contextKeyService.onDidChangeContext(() => this.setupMenubar())); + // Listen for changes in recently opened menu this._register(this.windowsService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); @@ -291,7 +308,7 @@ export class MenubarPart extends Part { this._register(ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this)); } - private setupMenubar(): void { + private doSetupMenubar(): void { if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { this.setupCustomMenubar(); } else { @@ -299,6 +316,10 @@ export class MenubarPart extends Part { } } + private setupMenubar(): void { + this.menuUpdater.schedule(); + } + private setupNativeMenubar(): void { // TODO@sbatten: Remove once native menubar is ready if (isMacintosh && isWindows) { @@ -428,6 +449,12 @@ export class MenubarPart extends Part { } private setupCustomMenubar(): void { + // Don't update while using the menu + if (this.isFocused) { + this.updatePending = true; + return; + } + this.container.empty(); this.container.attr('role', 'menubar'); @@ -773,7 +800,7 @@ export class MenubarPart extends Part { // Build the menubar if (this.container) { - this.setupMenubar(); + this.doSetupMenubar(); } return this.container.getHTMLElement(); From b44e7d9f0e4f473fd41f2610e20a4f35f10b2347 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 9 Jul 2018 11:26:16 -0700 Subject: [PATCH 066/151] fixes #52911 #53070 and aria roles with menuitem and submenuitems (#53289) --- src/vs/base/browser/ui/menu/menu.css | 7 ++ src/vs/base/browser/ui/menu/menu.ts | 139 ++++++++++++++++++++++----- 2 files changed, 120 insertions(+), 26 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index fff01a4a278..ef4b8cedfe4 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -39,6 +39,13 @@ background-color: #EEE; } +.monaco-menu .monaco-action-bar.vertical .action-menu-item { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + display: -ms-flexbox; + display: flex; +} + .monaco-menu .monaco-action-bar.vertical .action-label { -ms-flex: 1 1 auto; flex: 1 1 auto; diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 2eb053fbdc0..f26829f9edd 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -6,12 +6,13 @@ 'use strict'; import 'vs/css!./menu'; +import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; -import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { Event } from 'vs/base/common/event'; -import { addClass, EventType, EventHelper, EventLike } from 'vs/base/browser/dom'; +import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { $, Builder } from 'vs/base/browser/builder'; @@ -65,7 +66,7 @@ export class Menu { this.actionBar.push(actions, { icon: true, label: true, isMenu: true }); } - private doGetActionItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): ActionItem { + private doGetActionItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): BaseActionItem { if (action instanceof Separator) { return new ActionItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { @@ -110,42 +111,128 @@ export class Menu { } } -class MenuActionItem extends ActionItem { +class MenuActionItem extends BaseActionItem { static MNEMONIC_REGEX: RegExp = /&&(.)/g; + protected $e: Builder; + protected $label: Builder; + protected options: IActionItemOptions; + private cssClass: string; + constructor(ctx: any, action: IAction, options: IActionItemOptions = {}) { options.isMenu = true; super(action, action, options); - } - private _addMnemonic(action: IAction, actionItemElement: HTMLElement): void { - let matches = MenuActionItem.MNEMONIC_REGEX.exec(action.label); - if (matches && matches.length === 2) { - let mnemonic = matches[1]; - - let ariaLabel = action.label.replace(MenuActionItem.MNEMONIC_REGEX, mnemonic); - - actionItemElement.accessKey = mnemonic.toLocaleLowerCase(); - this.$e.attr('aria-label', ariaLabel); - } else { - this.$e.attr('aria-label', action.label); - } + this.options = options; + this.options.icon = options.icon !== undefined ? options.icon : false; + this.options.label = options.label !== undefined ? options.label : true; + this.cssClass = ''; } public render(container: HTMLElement): void { super.render(container); - this._addMnemonic(this.getAction(), container); - this.$e.attr('role', 'menuitem'); + this.$e = $('a.action-menu-item').appendTo(this.builder); + if (this._action.id === Separator.ID) { + // A separator is a presentation item + this.$e.attr({ role: 'presentation' }); + } else { + this.$e.attr({ role: 'menuitem' }); + } + + this.$label = $('span.action-label').appendTo(this.$e); + + if (this.options.label && this.options.keybinding) { + $('span.keybinding').text(this.options.keybinding).appendTo(this.$e); + } + + this._updateClass(); + this._updateLabel(); + this._updateTooltip(); + this._updateEnabled(); + this._updateChecked(); + } + + public focus(): void { + super.focus(); + this.$e.domFocus(); } public _updateLabel(): void { if (this.options.label) { let label = this.getAction().label; - if (label && this.options.isMenu) { + if (label) { + let matches = MenuActionItem.MNEMONIC_REGEX.exec(label); + if (matches && matches.length === 2) { + let mnemonic = matches[1]; + + let ariaLabel = label.replace(MenuActionItem.MNEMONIC_REGEX, mnemonic); + + this.$e.getHTMLElement().accessKey = mnemonic.toLocaleLowerCase(); + this.$label.attr('aria-label', ariaLabel); + } else { + this.$label.attr('aria-label', label); + } + label = label.replace(MenuActionItem.MNEMONIC_REGEX, '$1\u0332'); } - this.$e.text(label); + + this.$label.text(label); + } + } + + public _updateTooltip(): void { + let title: string = null; + + if (this.getAction().tooltip) { + title = this.getAction().tooltip; + + } else if (!this.options.label && this.getAction().label && this.options.icon) { + title = this.getAction().label; + + if (this.options.keybinding) { + title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding); + } + } + + if (title) { + this.$e.attr({ title: title }); + } + } + + public _updateClass(): void { + if (this.cssClass) { + this.$e.removeClass(this.cssClass); + } + if (this.options.icon) { + this.cssClass = this.getAction().class; + this.$label.addClass('icon'); + if (this.cssClass) { + this.$label.addClass(this.cssClass); + } + this._updateEnabled(); + } else { + this.$label.removeClass('icon'); + } + } + + public _updateEnabled(): void { + if (this.getAction().enabled) { + this.builder.removeClass('disabled'); + this.$e.removeClass('disabled'); + this.$e.attr({ tabindex: 0 }); + } else { + this.builder.addClass('disabled'); + this.$e.addClass('disabled'); + removeTabIndexAndUpdateFocus(this.$e.getHTMLElement()); + } + } + + public _updateChecked(): void { + if (this.getAction().checked) { + this.$label.addClass('checked'); + } else { + this.$label.removeClass('checked'); } } } @@ -167,10 +254,9 @@ class SubmenuActionItem extends MenuActionItem { public render(container: HTMLElement): void { super.render(container); - this.builder = $(container); - $(this.builder).addClass('monaco-submenu-item'); - $('span.submenu-indicator').text('\u25B6').appendTo(this.builder); - this.$e.attr('role', 'menu'); + this.$e.addClass('monaco-submenu-item'); + this.$e.attr('aria-haspopup', 'true'); + $('span.submenu-indicator').text('\u25B6').appendTo(this.$e); $(this.builder).on(EventType.KEY_UP, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); @@ -202,7 +288,6 @@ class SubmenuActionItem extends MenuActionItem { } }); - $(this.builder).on(EventType.MOUSE_LEAVE, (e) => { this.mouseOver = false; @@ -219,6 +304,8 @@ class SubmenuActionItem extends MenuActionItem { public onClick(e: EventLike) { // stop clicking from trying to run an action EventHelper.stop(e, true); + + this.createSubmenu(); } private cleanupExistingSubmenu(force: boolean) { From 0cadfe183451506803783af550cfd7de8c00e7b6 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 9 Jul 2018 11:41:13 -0700 Subject: [PATCH 067/151] fixes #53881 --- .../browser/parts/titlebar/titlebarPart.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index d7112d84a45..61f11d7209c 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -252,13 +252,15 @@ export class TitlebarPart extends Part implements ITitleService { // App Icon (Windows/Linux) if (!isMacintosh) { - this.appIcon = $(this.titleContainer).div({ - class: 'window-appicon', - }).on(EventType.DBLCLICK, e => { - EventHelper.stop(e, true); + this.appIcon = $(this.titleContainer).div({ class: 'window-appicon' }); - this.windowService.closeWindow().then(null, errors.onUnexpectedError); - }); + if (isWindows) { + this.appIcon.on(EventType.DBLCLICK, e => { + EventHelper.stop(e, true); + + this.windowService.closeWindow().then(null, errors.onUnexpectedError); + }); + } } // Title From d05663597c45c8abd21a1993f9e5a9ead7bf6e66 Mon Sep 17 00:00:00 2001 From: Itamar Kestenbaum Date: Mon, 9 Jul 2018 21:54:05 +0300 Subject: [PATCH 068/151] Simplify increment decrement of currentSignature --- src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 5a0de6b72b6..383ccff63cc 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -459,7 +459,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { return false; } - this.currentSignature = (this.currentSignature + 1) % length; + this.currentSignature++; this.render(); return true; } @@ -473,7 +473,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { return false; } - this.currentSignature = (this.currentSignature - 1 + length) % length; + this.currentSignature--; this.render(); return true; } From c3bc031c1acc83be92f4e0786ef2be30f10f1e2d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 9 Jul 2018 13:19:28 -0700 Subject: [PATCH 069/151] Fix run active file when no editor is focused Fixes #53019 --- .../parts/terminal/electron-browser/terminalActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 286854eba1b..c753ae9b9cc 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -625,7 +625,7 @@ export class RunActiveFileInTerminalAction extends Action { if (!instance) { return TPromise.as(void 0); } - const editor = this.codeEditorService.getFocusedCodeEditor(); + const editor = this.codeEditorService.getActiveCodeEditor(); if (!editor) { return TPromise.as(void 0); } From dd6874b8217150275e0e5ce6d60b7ac8fac5316c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 9 Jul 2018 14:44:47 -0700 Subject: [PATCH 070/151] Bump node2 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index ab3a77f657b..2831ba28d9c 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -6,7 +6,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.26.1", + "version": "1.26.2", "repo": "https://github.com/Microsoft/vscode-node-debug2" } ] From f7578b98f503e1e43d693ba9846298f6be7aedc6 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 9 Jul 2018 14:58:24 -0700 Subject: [PATCH 071/151] fix an issue with having to double click the menubar on context changes also greatly reduce the amount of recreation for menubar changes --- .../browser/parts/menubar/menubarPart.ts | 141 ++++++++++-------- 1 file changed, 80 insertions(+), 61 deletions(-) diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index e33dc07f208..8143f27c179 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -38,6 +38,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; interface CustomMenu { title: string; + buttonElement: Builder; titleElement: Builder; actions?: IAction[]; } @@ -271,10 +272,7 @@ export class MenubarPart extends Part { this.customMenus.forEach(customMenu => { let child = customMenu.titleElement.child(); if (child) { - let grandChild = child.child(); - if (grandChild) { - grandChild.style('text-decoration', modiferKeyStatus.altKey ? 'underline' : null); - } + child.style('text-decoration', modiferKeyStatus.altKey ? 'underline' : null); } }); } @@ -327,6 +325,11 @@ export class MenubarPart extends Part { } } + + private clearMnemonic(topLevelElement: HTMLElement): void { + topLevelElement.accessKey = null; + } + private registerMnemonic(topLevelElement: HTMLElement, mnemonic: string): void { topLevelElement.accessKey = mnemonic.toLocaleLowerCase(); } @@ -455,10 +458,12 @@ export class MenubarPart extends Part { return; } - this.container.empty(); this.container.attr('role', 'menubar'); - this.customMenus = []; + const firstTimeSetup = this.customMenus === undefined; + if (firstTimeSetup) { + this.customMenus = []; + } let idx = 0; @@ -466,26 +471,34 @@ export class MenubarPart extends Part { const menu: IMenu = this.topLevelMenus[menuTitle]; let menuIndex = idx++; - let titleElement = $(this.container).div({ class: 'menubar-menu-button' }); + // Create the top level menu button element + if (firstTimeSetup) { + const buttonElement = $(this.container).div({ class: 'menubar-menu-button' }).attr('role', 'menu'); + buttonElement.attr('aria-label', this.topLevelTitles[menuTitle].replace(/&&(.)/g, '$1')); + + const titleElement = $(buttonElement).div({ class: 'menubar-menu-title', 'aria-hidden': true }); + + this.customMenus.push({ + title: menuTitle, + buttonElement: buttonElement, + titleElement: titleElement + }); + } + + // Update the button label to reflect mnemonics let displayTitle = this.topLevelTitles[menuTitle].replace(/&&(.)/g, this.currentEnableMenuBarMnemonics ? '$1' : '$1'); - let legibleTitle = this.topLevelTitles[menuTitle].replace(/&&(.)/g, '$1'); - $(titleElement).div({ class: 'menubar-menu-title', 'aria-hidden': true }).innerHtml(displayTitle); - - titleElement.attr('aria-label', legibleTitle); - titleElement.attr('role', 'menu'); + $(this.customMenus[menuIndex].titleElement).innerHtml(displayTitle); + // Clear and register mnemonics due to updated settings + this.clearMnemonic(this.customMenus[menuIndex].buttonElement.getHTMLElement()); if (this.currentEnableMenuBarMnemonics) { let mnemonic = (/&&(.)/g).exec(this.topLevelTitles[menuTitle]); if (mnemonic && mnemonic[1]) { - this.registerMnemonic(titleElement.getHTMLElement(), mnemonic[1]); + this.registerMnemonic(this.customMenus[menuIndex].buttonElement.getHTMLElement(), mnemonic[1]); } } - this.customMenus.push({ - title: menuTitle, - titleElement: titleElement - }); - + // Update the menu actions const updateActions = (menu: IMenu, target: IAction[]) => { target.splice(0); let groups = menu.getActions(); @@ -513,53 +526,59 @@ export class MenubarPart extends Part { }; this.customMenus[menuIndex].actions = []; - this._register(menu.onDidChange(() => updateActions(menu, this.customMenus[menuIndex].actions))); + if (firstTimeSetup) { + this._register(menu.onDidChange(() => updateActions(menu, this.customMenus[menuIndex].actions))); + } + updateActions(menu, this.customMenus[menuIndex].actions); - this.customMenus[menuIndex].titleElement.on(EventType.CLICK, () => { - if (this._modifierKeyStatus && (this._modifierKeyStatus.shiftKey || this._modifierKeyStatus.ctrlKey)) { - return; // supress keyboard shortcuts that shouldn't conflict - } + if (firstTimeSetup) { + this.customMenus[menuIndex].buttonElement.on(EventType.CLICK, () => { + if (this._modifierKeyStatus && (this._modifierKeyStatus.shiftKey || this._modifierKeyStatus.ctrlKey)) { + return; // supress keyboard shortcuts that shouldn't conflict + } - this.toggleCustomMenu(menuIndex); - this.isFocused = !this.isFocused; - }); - - this.customMenus[menuIndex].titleElement.on(EventType.MOUSE_ENTER, () => { - if (this.isFocused && !this.isCurrentMenu(menuIndex)) { this.toggleCustomMenu(menuIndex); - } - }); + this.isFocused = !this.isFocused; + }); - this.customMenus[menuIndex].titleElement.on(EventType.MOUSE_LEAVE, () => { - if (!this.isFocused) { + this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_ENTER, () => { + if (this.isFocused && !this.isCurrentMenu(menuIndex)) { + this.toggleCustomMenu(menuIndex); + } + }); + + this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_LEAVE, () => { + if (!this.isFocused) { + this.cleanupCustomMenu(); + } + }); + + this.customMenus[menuIndex].buttonElement.on(EventType.BLUR, () => { this.cleanupCustomMenu(); - } - }); - - this.customMenus[menuIndex].titleElement.on(EventType.BLUR, () => { - this.cleanupCustomMenu(); - }); + }); + } } - this.container.off(EventType.KEY_DOWN); - this.container.on(EventType.KEY_DOWN, (e) => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); - let eventHandled = true; + if (firstTimeSetup) { + this.container.on(EventType.KEY_DOWN, (e) => { + let event = new StandardKeyboardEvent(e as KeyboardEvent); + let eventHandled = true; - if (event.equals(KeyCode.LeftArrow) || (event.shiftKey && event.keyCode === KeyCode.Tab)) { - this.focusPrevious(); - } else if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Tab)) { - this.focusNext(); - } else { - eventHandled = false; - } + if (event.equals(KeyCode.LeftArrow) || (event.shiftKey && event.keyCode === KeyCode.Tab)) { + this.focusPrevious(); + } else if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Tab)) { + this.focusNext(); + } else { + eventHandled = false; + } - if (eventHandled) { - event.preventDefault(); - event.stopPropagation(); - } - }); + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + }); + } } private focusPrevious(): void { @@ -670,9 +689,9 @@ export class MenubarPart extends Part { } } - customMenu.titleElement.domFocus(); + customMenu.buttonElement.domFocus(); - let menuHolder = $(customMenu.titleElement).div({ class: 'menubar-menu-items-holder' }); + let menuHolder = $(customMenu.buttonElement).div({ class: 'menubar-menu-items-holder' }); $(menuHolder.getHTMLElement().parentElement).addClass('open'); @@ -755,7 +774,7 @@ export class MenubarPart extends Part { } if (typeof this.initialSizing.menuButtonPaddingLeftRight !== 'number') { - this.initialSizing.menuButtonPaddingLeftRight = parseInt(this.customMenus[0].titleElement.getComputedStyle().paddingLeft, 10); + this.initialSizing.menuButtonPaddingLeftRight = parseInt(this.customMenus[0].buttonElement.getComputedStyle().paddingLeft, 10); } this.container.style({ @@ -766,7 +785,7 @@ export class MenubarPart extends Part { }); this.customMenus.forEach(customMenu => { - customMenu.titleElement.style({ + customMenu.buttonElement.style({ 'padding': `0 ${this.initialSizing.menuButtonPaddingLeftRight / browser.getZoomFactor()}px` }); }); @@ -783,8 +802,8 @@ export class MenubarPart extends Part { public getMenubarItemsDimensions(): Dimension { if (this.customMenus) { - const left = this.customMenus[0].titleElement.getHTMLElement().getBoundingClientRect().left; - const right = this.customMenus[this.customMenus.length - 1].titleElement.getHTMLElement().getBoundingClientRect().right; + const left = this.customMenus[0].buttonElement.getHTMLElement().getBoundingClientRect().left; + const right = this.customMenus[this.customMenus.length - 1].buttonElement.getHTMLElement().getBoundingClientRect().right; return new Dimension(right - left, this.container.getClientArea().height); } From 05cc253c82ad3fe95ca4371fd71ccc1c84cf0e20 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Mon, 9 Jul 2018 15:07:41 -0700 Subject: [PATCH 072/151] fixes #52239, allow for redirects from travis-ci.com --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 695afb3d60c..951ff212d9c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.26.0", - "distro": "7ee5cda9f4f44b2ba1f1ab290e48f86416914794", + "distro": "bf3aa85b8880d2d1c80030e21a3a2ddd384fa1e7", "author": { "name": "Microsoft Corporation" }, From 4e91c57764b7686df8670e819dea91a2a817eb18 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 9 Jul 2018 15:38:53 -0700 Subject: [PATCH 073/151] Fix bug causing recommendations to sometimes appear in two places (#53906) * Fix bug causing recommendations to sometimes appear in two places * Naming * Group fetch stage calls --- .../electron-browser/extensionTipsService.ts | 25 ++++++++----------- .../extensionsTipsService.test.ts | 20 +++++++-------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 784c508d624..fbaf724d4ad 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -81,7 +81,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe private _workspaceIgnoredRecommendations: string[] = []; private _extensionsRecommendationsUrl: string; private _disposables: IDisposable[] = []; - public loadRecommendationsPromise: TPromise; + public loadWorkspaceConfigPromise: TPromise; private proactiveRecommendationsFetched: boolean = false; private readonly _onRecommendationChange: Emitter = new Emitter(); @@ -120,22 +120,19 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let globallyIgnored = JSON.parse(this.storageService.get('extensionsAssistant/ignored_recommendations', StorageScope.GLOBAL, '[]')); this._globallyIgnoredRecommendations = globallyIgnored.map(id => id.toLowerCase()); - this.loadRecommendationsPromise = this.getWorkspaceRecommendations() - .then(() => { - // these must be called after workspace configs have been refreshed. - this.fetchCachedDynamicWorkspaceRecommendations(); - this.fetchFileBasedRecommendations(); - this.fetchExperimentalRecommendations(); - return this.promptWorkspaceRecommendations(); - }).then(() => { - this._modelService.onModelAdded(this.promptFiletypeBasedRecommendations, this, this._disposables); - this._modelService.getModels().forEach(model => this.promptFiletypeBasedRecommendations(model)); - }); - + this.fetchCachedDynamicWorkspaceRecommendations(); + this.fetchFileBasedRecommendations(); + this.fetchExperimentalRecommendations(); if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { this.fetchProactiveRecommendations(true); } + this.loadWorkspaceConfigPromise = this.getWorkspaceRecommendations().then(() => { + this.promptWorkspaceRecommendations(); + this._modelService.onModelAdded(this.promptFiletypeBasedRecommendations, this, this._disposables); + this._modelService.getModels().forEach(model => this.promptFiletypeBasedRecommendations(model)); + }); + this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e))); this._register(this.configurationService.onDidChangeConfiguration(e => { if (!this.proactiveRecommendationsFetched && !this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { @@ -411,7 +408,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (this._exeBasedRecommendations[extensionId]) { sources.push('executable'); } - if (this._dynamicWorkspaceRecommendations[extensionId]) { + if (this._dynamicWorkspaceRecommendations.indexOf(extensionId) !== -1) { sources.push('dynamic'); } return ({ extensionId, sources }); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index faa0bf3f29f..89cbd28c3d6 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -287,7 +287,7 @@ suite('ExtensionsTipsService Test', () => { function testNoPromptForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', recommendations).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, recommendations.length); assert.ok(!prompted); }); @@ -297,7 +297,7 @@ suite('ExtensionsTipsService Test', () => { function testNoPromptOrRecommendationsForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - assert.equal(!testObject.loadRecommendationsPromise, true); + assert.equal(!testObject.loadWorkspaceConfigPromise, true); assert.ok(!prompted); return testObject.getWorkspaceRecommendations().then(() => { @@ -327,7 +327,7 @@ suite('ExtensionsTipsService Test', () => { test('ExtensionTipsService: Prompt for valid workspace recommendations', () => { return setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); assert.equal(recommendations.length, mockTestData.validRecommendedExtensions.length); @@ -359,7 +359,7 @@ suite('ExtensionsTipsService Test', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, 0); assert.ok(!prompted); }); @@ -387,7 +387,7 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(!recommendations['ms-vscode.csharp']); // stored recommendation that has been globally ignored assert.ok(recommendations['ms-python.python']); // stored recommendation @@ -407,7 +407,7 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(!recommendations['ms-vscode.csharp']); // stored recommendation that has been workspace ignored assert.ok(recommendations['ms-python.python']); // stored recommendation @@ -435,7 +435,7 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(recommendations['ms-python.python']); @@ -462,7 +462,7 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(recommendations['ms-python.python']); assert.ok(recommendations['mockpublisher1.mockextension1']); @@ -516,7 +516,7 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-vscode.csharp')); // stored recommendation that exists in product.extensionTips @@ -535,7 +535,7 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.loadRecommendationsPromise.then(() => { + return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-vscode.csharp')); // stored recommendation that exists in product.extensionTips From dc137ce28f4d4c16af67a6d7fba3629d0c8c985e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Jul 2018 18:55:23 -0700 Subject: [PATCH 074/151] Move isTypeScriptDocument to languageModes --- .../src/features/fileConfigurationManager.ts | 8 ++------ .../src/utils/languageModeIds.ts | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index 0cb8c3da539..9bdc174b858 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -7,8 +7,8 @@ import { workspace as Workspace, FormattingOptions, TextDocument, CancellationTo import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; -import * as languageIds from '../utils/languageModeIds'; import API from '../utils/api'; +import { isTypeScriptDocument } from '../utils/languageModeIds'; function objsAreEqual(a: T, b: T): boolean { let keys = Object.keys(a); @@ -175,8 +175,4 @@ function getImportModuleSpecifierPreference(config: WorkspaceConfiguration) { case 'non-relative': return 'non-relative'; default: return undefined; } -} - -function isTypeScriptDocument(document: TextDocument) { - return document.languageId === languageIds.typescript || document.languageId === languageIds.typescriptreact; -} +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/languageModeIds.ts b/extensions/typescript-language-features/src/utils/languageModeIds.ts index 5fe187e13db..9be48067eda 100644 --- a/extensions/typescript-language-features/src/utils/languageModeIds.ts +++ b/extensions/typescript-language-features/src/utils/languageModeIds.ts @@ -14,4 +14,8 @@ export const jsxTags = 'jsx-tags'; export function isSupportedLanguageMode(doc: vscode.TextDocument) { return vscode.languages.match([typescript, typescriptreact, javascript, javascriptreact], doc) > 0; +} + +export function isTypeScriptDocument(doc: vscode.TextDocument) { + return vscode.languages.match([typescript, typescriptreact], doc) > 0; } \ No newline at end of file From adfa9ce9778a6e79197477fcc60cb5420a31979a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Jul 2018 18:59:59 -0700 Subject: [PATCH 075/151] Add initial support for auto close jsx tags Fixes #34307 --- .../typescript-language-features/package.json | 10 ++ .../package.nls.json | 3 +- .../src/features/tagCompletion.ts | 156 ++++++++++++++---- .../src/languageProvider.ts | 2 +- .../src/utils/dependentRegistration.ts | 2 +- 5 files changed, 137 insertions(+), 36 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 841024fd02b..bd31ca0764d 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -505,6 +505,16 @@ "default": "prompt", "description": "%typescript.updateImportsOnFileMove.enabled%", "scope": "resource" + }, + "typescript.autoClosingTags": { + "type": "boolean", + "default": true, + "description": "%typescript.autoClosingTags%" + }, + "javascript.autoClosingTags": { + "type": "boolean", + "default": true, + "description": "%typescript.autoClosingTags%" } } }, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index b221d624d26..6876a3706ab 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -55,5 +55,6 @@ "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: 'single' quotes, 'double' quotes, or 'auto' infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.", "typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports:\n- \"relative\" to the file location.\n- \"non-relative\" based on the 'baseUrl' configured in your 'jsconfig.json' / 'tsconfig.json'.\n- \"auto\" infer the shortest path type.\nRequires using TypeScript 2.9 or newer in the workspace.", "typescript.showUnused": "Enable/disable highlighting of unused variables in code. Requires using TypeScript 2.9 or newer in the workspace.", - "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Possible values are: 'prompt' on each rename, 'always' update paths automatically, and 'never' rename paths and don't prompt me. Requires using TypeScript 2.9 or newer in the workspace." + "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Possible values are: 'prompt' on each rename, 'always' update paths automatically, and 'never' rename paths and don't prompt me. Requires using TypeScript 2.9 or newer in the workspace.", + "typescript.autoClosingTags": "Enable/disable automatic closing of JSX tags. Requires using TypeScript 3.0 or newer in the workspace." } diff --git a/extensions/typescript-language-features/src/features/tagCompletion.ts b/extensions/typescript-language-features/src/features/tagCompletion.ts index 8a8495180ca..bcc145d9089 100644 --- a/extensions/typescript-language-features/src/features/tagCompletion.ts +++ b/extensions/typescript-language-features/src/features/tagCompletion.ts @@ -7,44 +7,104 @@ import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { VersionDependentRegistration, ConfigurationDependentRegistration, ConditionalRegistration } from '../utils/dependentRegistration'; +import { disposeAll } from '../utils/dispose'; import * as typeConverters from '../utils/typeConverters'; -class TypeScriptTagCompletion implements vscode.CompletionItemProvider { +class TagClosing { + + private _disposed = false; + private timeout: NodeJS.Timer | undefined = undefined; + + private readonly disposables: vscode.Disposable[] = []; + constructor( private readonly client: ITypeScriptServiceClient - ) { } + ) { + vscode.workspace.onDidChangeTextDocument( + event => this.onDidChangeTextDocument(event.document, event.contentChanges), + null, this.disposables); - async provideCompletionItems( - document: vscode.TextDocument, - position: vscode.Position, - token: vscode.CancellationToken, - _context: vscode.CompletionContext - ): Promise { - const filepath = this.client.toPath(document.uri); - if (!filepath) { - return undefined; - } + vscode.window.onDidChangeActiveTextEditor( + () => this.updateEnabledState(), + null, this.disposables); - const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - let body: Proto.TextInsertion | undefined = undefined; - try { - const response = await this.client.execute('jsxClosingTag', args, token); - body = response && response.body; - if (!body) { - return undefined; - } - } catch { - return undefined; - } - - return [this.getCompletion(body)]; + this.updateEnabledState(); } - private getCompletion(body: Proto.TextInsertion) { - const completion = new vscode.CompletionItem(body.newText); - completion.insertText = this.getTagSnippet(body); - return completion; + public dispose() { + disposeAll(this.disposables); + this._disposed = true; + this.timeout = undefined; + } + + private updateEnabledState() { + + } + + private onDidChangeTextDocument( + document: vscode.TextDocument, + changes: vscode.TextDocumentContentChangeEvent[] + ) { + const activeDocument = vscode.window.activeTextEditor && vscode.window.activeTextEditor.document; + if (document !== activeDocument || changes.length === 0) { + return; + } + + const filepath = this.client.toPath(document.uri); + if (!filepath) { + return; + } + + if (typeof this.timeout !== 'undefined') { + clearTimeout(this.timeout); + } + + const lastChange = changes[changes.length - 1]; + const lastCharacter = lastChange.text[lastChange.text.length - 1]; + if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') { + return; + } + + const rangeStart = lastChange.range.start; + const version = document.version; + this.timeout = setTimeout(async () => { + if (this._disposed) { + return; + } + + let position = new vscode.Position(rangeStart.line, rangeStart.character + lastChange.text.length); + let body: Proto.TextInsertion | undefined = undefined; + const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + + try { + const response = await this.client.execute('jsxClosingTag', args, null as any); + body = response && response.body; + if (!body) { + return; + } + } catch { + return; + } + + if (!this._disposed) { + const activeEditor = vscode.window.activeTextEditor; + if (activeEditor) { + const activeDocument = activeEditor.document; + if (document === activeDocument && activeDocument.version === version) { + const selections = activeEditor.selections; + const snippet = this.getTagSnippet(body); + if (selections.length && selections.some(s => s.active.isEqual(position))) { + activeEditor.insertSnippet(snippet, selections.map(s => s.active)); + } else { + activeEditor.insertSnippet(snippet, position); + } + } + } + } + + this.timeout = void 0; + }, 100); } private getTagSnippet(closingTag: Proto.TextInsertion): vscode.SnippetString { @@ -55,12 +115,42 @@ class TypeScriptTagCompletion implements vscode.CompletionItemProvider { } } +export class ActiveDocumentDependentRegistration { + private readonly _registration: ConditionalRegistration; + private readonly _disposables: vscode.Disposable[] = []; + + constructor( + private readonly selector: vscode.DocumentSelector, + register: () => vscode.Disposable, + ) { + this._registration = new ConditionalRegistration(register); + + this.update(); + + vscode.window.onDidChangeActiveTextEditor(() => { + this.update(); + }, null, this._disposables); + } + + public dispose() { + disposeAll(this._disposables); + this._registration.dispose(); + } + + private update() { + const editor = vscode.window.activeTextEditor; + const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); + this._registration.update(enabled); + } +} + export function register( selector: vscode.DocumentSelector, + modeId: string, client: ITypeScriptServiceClient, ) { return new VersionDependentRegistration(client, API.v300, () => - vscode.languages.registerCompletionItemProvider(selector, - new TypeScriptTagCompletion(client), - '>')); + new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () => + new ActiveDocumentDependentRegistration(selector, () => + new TagClosing(client)))); } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 2d7cde5e693..c963ef6586c 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -91,7 +91,7 @@ export default class LanguageProvider { this.disposables.push((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); this.disposables.push((await import('./features/rename')).register(selector, this.client)); this.disposables.push((await import('./features/signatureHelp')).register(selector, this.client)); - this.disposables.push((await import('./features/tagCompletion')).register(selector, this.client)); + this.disposables.push((await import('./features/tagCompletion')).register(selector, this.description.id, this.client)); this.disposables.push((await import('./features/typeDefinitions')).register(selector, this.client)); this.disposables.push((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); } diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 9013dd28bc9..7ffe3c4195b 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -8,7 +8,7 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from './api'; import { disposeAll } from './dispose'; -class ConditionalRegistration { +export class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; public constructor( From a486f518d62d5cbc3c46536f73c45e659f48301e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 09:46:43 +0200 Subject: [PATCH 076/151] async execution order tests related to #53773 --- src/vs/base/test/common/async.test.ts | 89 +++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 9cab77a9881..24ef5347f56 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -41,6 +41,95 @@ suite('Async', () => { return result; }); + // 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 = []; + + 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(null, err => null) + .then(() => order.push('finally')); + + cancellablePromise.cancel(); + order.push('afterCancel'); + + return promise.then(() => assert.deepEqual(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 = []; + + const cancellablePromise = async.createCancelablePromise(token => { + order.push('in callback'); + token.onCancellationRequested(_ => order.push('cancelled')); + return new Promise(c => setTimeout(c(1234), 0)); + }); + + order.push('afterCreate'); + + const promise = cancellablePromise + .then(null, err => null) + .then(() => order.push('finally')); + + cancellablePromise.cancel(); + order.push('afterCancel'); + + return promise.then(() => assert.deepEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); + }); + + // Cancelling a sync tpromise will NOT cancel the promise, since it has resolved already. + // Every `then` callback runs sync in the same execution frame, thus `finally` executes + // before `afterCancel`. + test('TPromise execution order (sync)', function () { + const order = []; + let promise = new TPromise(resolve => { + order.push('in executor'); + resolve(1234); + }, () => order.push('cancelled')); + + order.push('afterCreate'); + + promise = promise + .then(null, err => null) + .then(() => order.push('finally')); + + promise.cancel(); + order.push('afterCancel'); + + return promise.then(() => assert.deepEqual(order, ['in executor', 'afterCreate', 'finally', 'afterCancel'])); + }); + + // Cancelling an async tpromise will cancel the promise. + // Every `then` callback runs sync on the same execution frame as the `cancel` call, + // so finally still executes before `afterCancel`. + test('TPromise execution order (async)', function () { + const order = []; + let promise = new TPromise(resolve => { + order.push('in executor'); + setTimeout(() => resolve(1234)); + }, () => order.push('cancelled')); + + order.push('afterCreate'); + + promise = promise + .then(null, err => null) + .then(() => order.push('finally')); + + promise.cancel(); + order.push('afterCancel'); + + return promise.then(() => assert.deepEqual(order, ['in executor', 'afterCreate', 'cancelled', 'finally', 'afterCancel'])); + }); + test('cancelablePromise - get inner result', async function () { let promise = async.createCancelablePromise(token => { return async.timeout(12).then(_ => 1234); From 0d605987656a91cbc64c27deacf98e3f33c61d00 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 10:02:45 +0200 Subject: [PATCH 077/151] safeguard list splice calls related to #53773 --- src/vs/base/browser/ui/list/listWidget.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index d297818a787..acdcb44f5d8 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -929,6 +929,14 @@ export class List implements ISpliceable, IDisposable { } splice(start: number, deleteCount: number, elements: T[] = []): void { + if (start < 0 || start > this.view.length) { + throw new Error(`Invalid start index: ${start}`); + } + + if (deleteCount < 0) { + throw new Error(`Invalid delete count: ${deleteCount}`); + } + if (deleteCount === 0 && elements.length === 0) { return; } From 6511d05ec8f680d2fde87b4b5a1d909f78120697 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 10:05:22 +0200 Subject: [PATCH 078/151] fixes #53773 --- src/vs/editor/contrib/suggest/suggestWidget.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 9361c2b2422..e46dd9613e1 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -601,7 +601,11 @@ export class SuggestWidget implements IContentWidget, IDelegate } else { removeClass(this.element, 'docs-side'); } - }).catch(onUnexpectedError).then(() => this.currentSuggestionDetails = null); + }).catch(onUnexpectedError).then(() => { + if (this.focusedItem === item) { + this.currentSuggestionDetails = null; + } + }); // emit an event this.onDidFocusEmitter.fire({ item, index, model: this.completionModel }); From 00260009e8d9f3f6e7326bab3e09b1c932022e59 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 10:31:48 +0200 Subject: [PATCH 079/151] fixes #53898 --- .../parts/scm/electron-browser/media/dirtydiffDecorator.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css b/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css index e55cb63c612..de4d95ed717 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css @@ -19,6 +19,7 @@ border-top: 4px solid transparent; border-bottom: 4px solid transparent; transition: border-top-width 80ms linear, border-bottom-width 80ms linear, bottom 80ms linear; + pointer-events: none; } .monaco-editor .dirty-diff-glyph:before { From 69760cd7c293980960cb9f37db5552b3162461c7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 11:02:02 +0200 Subject: [PATCH 080/151] check for disposed buffer --- src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts index bb1e22f0e68..f613d656546 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts @@ -117,7 +117,7 @@ export class EditorBreadcrumbsModel { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { - if (versionIdThen === buffer.getVersionId()) { + if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); } }, 150); From 09f4fae47e393311f6268b644c398d1597a2bee7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 11:08:22 +0200 Subject: [PATCH 081/151] focus first element in tree --- src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index d271ac2a475..f4dc0874688 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -41,6 +41,7 @@ import { EditorInput } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { onUnexpectedError } from 'vs/base/common/errors'; class Item extends BreadcrumbsItem { @@ -296,8 +297,10 @@ export abstract class BreadcrumbsPicker { this.focus = dom.trackFocus(this._domNode); this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); - - this._tree.setInput(this._getInput(input)); + this._tree.setInput(this._getInput(input)).then(_ => { + this._tree.domFocus(); + this._tree.focusFirst(); + }, onUnexpectedError); } dispose(): void { From b4ed5bdc5fa1b4de193ea80e2be03944594ea46f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 12:07:34 +0200 Subject: [PATCH 082/151] use Shift-keybindings to workaround eager list/tree keybindings, move picker when visible and focus changes --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 13 ++++-- .../browser/parts/editor/editorBreadcrumbs.ts | 46 +++++++++++++------ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 29cee352905..aa7d97bb081 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -49,6 +49,7 @@ export interface IBreadcrumbsWidgetStyles { } export interface IBreadcrumbsItemEvent { + type: 'select' | 'focus'; item: BreadcrumbsItem; node: HTMLElement; } @@ -139,6 +140,10 @@ export class BreadcrumbsWidget { this._domNode.focus(); } + isDOMFocused(): boolean { + return this._domNode === document.activeElement; + } + getFocused(): BreadcrumbsItem { return this._items[this._focusedItemIdx]; } @@ -149,12 +154,12 @@ export class BreadcrumbsWidget { focusPrev(): any { this._focus((this._focusedItemIdx - 1 + this._nodes.length) % this._nodes.length); - this._domNode.focus(); + // this._domNode.focus(); } focusNext(): any { this._focus((this._focusedItemIdx + 1) % this._nodes.length); - this._domNode.focus(); + // this._domNode.focus(); } private _focus(nth: number): void { @@ -168,7 +173,7 @@ export class BreadcrumbsWidget { dom.addClass(node, 'focused'); } } - this._onDidFocusItem.fire({ item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx] }); + this._onDidFocusItem.fire({ type: 'focus', item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx] }); } getSelected(): BreadcrumbsItem { @@ -190,7 +195,7 @@ export class BreadcrumbsWidget { dom.addClass(node, 'selected'); } } - this._onDidSelectItem.fire({ item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx] }); + this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx] }); } setItems(items: BreadcrumbsItem[]): void { diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts index f4dc0874688..542a8fb6294 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts @@ -11,7 +11,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { compareFileNames } from 'vs/base/common/comparers'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { dirname, isEqual } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -99,10 +99,10 @@ class Item extends BreadcrumbsItem { export class EditorBreadcrumbs implements IEditorBreadcrumbs { static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); - static CK_BreadcrumbsFocused = new RawContextKey('breadcrumbsFocused', false); + static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false); private readonly _ckBreadcrumbsVisible: IContextKey; - private readonly _ckBreadcrumbsFocused: IContextKey; + private readonly _ckBreadcrumbsActive: IContextKey; private readonly _cfEnabled: Config; @@ -111,6 +111,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { private readonly _widget: BreadcrumbsWidget; private _breadcrumbsDisposables = new Array(); + private _breadcrumbsPickerShowing = false; constructor( container: HTMLElement, @@ -130,7 +131,8 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._widget = new BreadcrumbsWidget(this._domNode); this._widget.onDidSelectItem(this._onDidSelectItem, this, this._disposables); - this._widget.onDidChangeFocus(val => this._ckBreadcrumbsFocused.set(val), undefined, this._disposables); + this._widget.onDidFocusItem(this._onDidSelectItem, this, this._disposables); + this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables); this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService)); this._cfEnabled = Config.create(configurationService, 'breadcrumbs.enabled'); @@ -145,7 +147,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { })); this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); - this._ckBreadcrumbsFocused = EditorBreadcrumbs.CK_BreadcrumbsFocused.bindTo(this._contextKeyService); + this._ckBreadcrumbsActive = EditorBreadcrumbs.CK_BreadcrumbsActive.bindTo(this._contextKeyService); } dispose(): void { @@ -222,6 +224,11 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { return; } + if (event.type === 'focus' && !this._breadcrumbsPickerShowing) { + // focus change only moves the picker when already active + return; + } + this._editorGroup.focus(); this._contextViewService.showContextView({ getAnchor() { @@ -236,7 +243,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; let res = this._instantiationService.createInstance(ctor, container, element); res.layout({ width: 250, height: 300 }); - res.onDidPickElement(data => { + let listener = res.onDidPickElement(data => { this._contextViewService.hideContextView(); if (!data) { return; @@ -261,14 +268,22 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { } }); - return res; + this._breadcrumbsPickerShowing = true; + this._updateCkBreadcrumbsActive(); + + return combinedDisposable([listener, res]); }, - onHide: () => { - this._widget.setSelected(undefined); - // this._widget.setFocused(undefined); + onHide: (data) => { + this._breadcrumbsPickerShowing = false; + this._updateCkBreadcrumbsActive(); } }); } + + private _updateCkBreadcrumbsActive(): void { + const value = this._widget.isDOMFocused() || this._breadcrumbsPickerShowing; + this._ckBreadcrumbsActive.set(value); + } } export abstract class BreadcrumbsPicker { @@ -512,7 +527,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focusNext', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.RightArrow, - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + secondary: [KeyMod.Shift | KeyCode.RightArrow], + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), handler(accessor) { let groups = accessor.get(IEditorGroupsService); groups.activeGroup.breadcrumbs.focusNext(); @@ -522,7 +538,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focusPrevious', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.LeftArrow, - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + secondary: [KeyMod.Shift | KeyCode.LeftArrow], + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), handler(accessor) { let groups = accessor.get(IEditorGroupsService); groups.activeGroup.breadcrumbs.focusPrev(); @@ -533,7 +550,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.Enter, secondary: [KeyCode.UpArrow, KeyCode.Space], - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), handler(accessor) { let groups = accessor.get(IEditorGroupsService); groups.activeGroup.breadcrumbs.select(); @@ -543,7 +560,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.selectEditor', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.Escape, - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsFocused), + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), handler(accessor) { let groups = accessor.get(IEditorGroupsService); groups.activeGroup.activeControl.focus(); From 6957a5c05844706e9ca8b9ae18a2af8f946f1556 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 12:37:25 +0200 Subject: [PATCH 083/151] make git activation async fixes #50292 --- extensions/git/src/api.ts | 21 +++++--- extensions/git/src/main.ts | 107 +++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 66 deletions(-) diff --git a/extensions/git/src/api.ts b/extensions/git/src/api.ts index 6832fda9531..cfce4c9ed8b 100644 --- a/extensions/git/src/api.ts +++ b/extensions/git/src/api.ts @@ -42,19 +42,24 @@ export interface API { export class APIImpl implements API { - constructor(private modelPromise: Promise) { } + constructor(private model: Model) { } async getGitPath(): Promise { - const model = await this.modelPromise; - return model.git.path; + return this.model.git.path; } async getRepositories(): Promise { - const model = await this.modelPromise; - return model.repositories.map(repository => new RepositoryImpl(repository)); + return this.model.repositories.map(repository => new RepositoryImpl(repository)); } } -export function createApi(modelPromise: Promise): API { - return new APIImpl(modelPromise); -} \ No newline at end of file +export class NoopAPIImpl implements API { + + async getGitPath(): Promise { + throw new Error('Git model not found'); + } + + async getRepositories(): Promise { + throw new Error('Git model not found'); + } +} diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index fa155966bbf..9b1d988c842 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -16,12 +16,18 @@ import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { API, createApi } from './api'; +import { API, NoopAPIImpl, APIImpl } from './api'; import { GitProtocolHandler } from './protocolHandler'; -let telemetryReporter: TelemetryReporter; +const deactivateTasks: { (): Promise; }[] = []; -async function init(context: ExtensionContext, outputChannel: OutputChannel, disposables: Disposable[]): Promise { +export async function deactivate(): Promise { + for (const task of deactivateTasks) { + await task(); + } +} + +async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { const pathHint = workspace.getConfiguration('git').get('path'); const info = await findGit(pathHint, path => outputChannel.appendLine(localize('looking', "Looking for git in: {0}", path))); const askpass = new Askpass(); @@ -63,13 +69,32 @@ async function init(context: ExtensionContext, outputChannel: OutputChannel, dis return model; } -async function _activate(context: ExtensionContext, disposables: Disposable[]): Promise { +export async function activate(context: ExtensionContext): Promise { + const disposables: Disposable[] = []; + context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); + const outputChannel = window.createOutputChannel('Git'); commands.registerCommand('git.showOutput', () => outputChannel.show()); disposables.push(outputChannel); + const { name, version, aiKey } = require(context.asAbsolutePath('./package.json')) as { name: string, version: string, aiKey: string }; + const telemetryReporter = new TelemetryReporter(name, version, aiKey); + deactivateTasks.push(() => telemetryReporter.dispose()); + + await new Promise(c => setTimeout(c, 10000)); + + const config = workspace.getConfiguration('git', null); + const enabled = config.get('enabled'); + + if (!enabled) { + const onConfigChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')); + const onEnabled = filterEvent(onConfigChange, () => workspace.getConfiguration('git', null).get('enabled') === true); + await eventToPromise(onEnabled); + } + try { - return await init(context, outputChannel, disposables); + const model = await createModel(context, outputChannel, telemetryReporter, disposables); + return new APIImpl(model); } catch (err) { if (!/Git installation not found/.test(err.message || '')) { throw err; @@ -78,60 +103,30 @@ async function _activate(context: ExtensionContext, disposables: Disposable[]): const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreMissingGitWarning') === true; - if (shouldIgnore) { - return; + if (!shouldIgnore) { + console.warn(err.message); + outputChannel.appendLine(err.message); + outputChannel.show(); + + const download = localize('downloadgit', "Download Git"); + const neverShowAgain = localize('neverShowAgain', "Don't Show Again"); + const choice = await window.showWarningMessage( + localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."), + download, + neverShowAgain + ); + + if (choice === download) { + commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); + } else if (choice === neverShowAgain) { + await config.update('ignoreMissingGitWarning', true, true); + } } - console.warn(err.message); - outputChannel.appendLine(err.message); - outputChannel.show(); - - const download = localize('downloadgit', "Download Git"); - const neverShowAgain = localize('neverShowAgain', "Don't Show Again"); - const choice = await window.showWarningMessage( - localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."), - download, - neverShowAgain - ); - - if (choice === download) { - commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); - } else if (choice === neverShowAgain) { - await config.update('ignoreMissingGitWarning', true, true); - } + return new NoopAPIImpl(); } } -export function activate(context: ExtensionContext): API { - const config = workspace.getConfiguration('git', null); - const enabled = config.get('enabled'); - - const disposables: Disposable[] = []; - context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); - - const { name, version, aiKey } = require(context.asAbsolutePath('./package.json')) as { name: string, version: string, aiKey: string }; - telemetryReporter = new TelemetryReporter(name, version, aiKey); - - let activatePromise: Promise; - - if (enabled) { - activatePromise = _activate(context, disposables); - } else { - const onConfigChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')); - const onEnabled = filterEvent(onConfigChange, () => workspace.getConfiguration('git', null).get('enabled') === true); - - activatePromise = eventToPromise(onEnabled) - .then(() => _activate(context, disposables)); - } - - const modelPromise = activatePromise - .then(model => model || Promise.reject('Git model not found')); - - activatePromise.catch(err => console.error(err)); - - return createApi(modelPromise); -} - async function checkGitVersion(info: IGit): Promise { const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLegacyWarning') === true; @@ -159,7 +154,3 @@ async function checkGitVersion(info: IGit): Promise { await config.update('ignoreLegacyWarning', true, true); } } - -export function deactivate(): Promise { - return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null); -} From c54eafd15696209bbacdc56bb99416d379872081 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 12:50:20 +0200 Subject: [PATCH 084/151] tfs: use build templates --- build/tfs/darwin/product-build-darwin.yml | 63 ++++ build/tfs/linux/product-build-linux.yml | 49 +++ build/tfs/product-build.yml | 344 +--------------------- build/tfs/win32/product-build-win32.yml | 162 ++++++++++ 4 files changed, 289 insertions(+), 329 deletions(-) create mode 100644 build/tfs/darwin/product-build-darwin.yml create mode 100644 build/tfs/linux/product-build-linux.yml create mode 100644 build/tfs/win32/product-build-win32.yml diff --git a/build/tfs/darwin/product-build-darwin.yml b/build/tfs/darwin/product-build-darwin.yml new file mode 100644 index 00000000000..0dd31503586 --- /dev/null +++ b/build/tfs/darwin/product-build-darwin.yml @@ -0,0 +1,63 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.9.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.3.2" + +- script: | + set -e + echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc + yarn + npm run gulp -- hygiene + npm run monaco-compile-check + VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin + node build/tfs/common/installDistro.js + node build/lib/builtInExtensions.js + +- script: | + set -e + VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ + AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ + npm run gulp -- vscode-darwin-min upload-vscode-sourcemaps + name: build + +- script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`" + # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME" + name: test + +- script: | + set -e + # archive the unsigned build + pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin-unsigned.zip * && popd + + # publish the unsigned build + PACKAGEJSON=`ls ../VSCode-darwin/*.app/Contents/Resources/app/package.json` + VERSION=`node -p "require(\"$PACKAGEJSON\").version"` + AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + node build/tfs/common/publish.js \ + "$(VSCODE_QUALITY)" \ + darwin \ + archive-unsigned \ + "VSCode-darwin-$(VSCODE_QUALITY)-unsigned.zip" \ + $VERSION \ + false \ + ../VSCode-darwin-unsigned.zip + + # publish hockeyapp symbols + node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_MACOS)" + + # enqueue the unsigned build + AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + node build/tfs/darwin/enqueue.js "$(VSCODE_QUALITY)" + + AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ + npm run gulp -- upload-vscode-configuration \ No newline at end of file diff --git a/build/tfs/linux/product-build-linux.yml b/build/tfs/linux/product-build-linux.yml new file mode 100644 index 00000000000..b5baa02e143 --- /dev/null +++ b/build/tfs/linux/product-build-linux.yml @@ -0,0 +1,49 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.9.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.3.2" + +- script: | + set -e + export npm_config_arch="$(VSCODE_ARCH)" + if [[ "$(VSCODE_ARCH)" == "ia32" ]]; then + export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" + fi + + echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc + yarn + npm run gulp -- hygiene + npm run monaco-compile-check + VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin + node build/tfs/common/installDistro.js + node build/lib/builtInExtensions.js + +- script: | + set -e + VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- vscode-linux-$(VSCODE_ARCH)-min + name: build + +- script: | + set -e + npm run gulp -- "electron-$(VSCODE_ARCH)" + DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" + # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)" + name: test + +- script: | + set -e + npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" + npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" + #npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-snap" + + AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + ./build/tfs/linux/release.sh "$(VSCODE_ARCH)" "$(LINUX_REPO_PASSWORD)" + + # publish hockeyapp symbols + node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX64)" diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index e0ac588a85a..f76b9c42a49 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -1,353 +1,39 @@ phases: - phase: Windows condition: eq(variables['VSCODE_BUILD_WIN32'], 'true') - queue: - name: Hosted VS2017 - parallel: 2 - matrix: - x64: - VSCODE_ARCH: x64 - ia32: - VSCODE_ARCH: ia32 - + queue: Hosted VS2017 + variables: + VSCODE_ARCH: ia32 steps: - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" + - template: build/tfs/win32/product-build-win32.yml - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - - powershell: | - $ErrorActionPreference = "Stop" - "machine monacotools.visualstudio.com password $(VSO_PAT)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" - yarn - npm run gulp -- hygiene - npm run monaco-compile-check - $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" - npm run gulp -- mixin - node build/tfs/common/installDistro.js - node build/lib/builtInExtensions.js - - - powershell: | - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" - npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-min" - npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-copy-inno-updater" - name: build - - - powershell: | - $ErrorActionPreference = "Stop" - npm run gulp -- "electron-$(VSCODE_ARCH)" - .\scripts\test.bat --build --tfs "Unit Tests" - # yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - name: test - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)' - Pattern: '*.dll,*.exe,*.node' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-229803", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/t \"http://ts4096.gtm.microsoft.com/TSS/AuthenticodeTS\"" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "Append", - "parameterValue": "/as" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ - { - "parameterName": "VerifyAll", - "parameterValue": "/all" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 - - - task: NuGetCommand@2 - displayName: Install ESRPClient.exe - inputs: - restoreSolution: 'build\tfs\win32\ESRPClient\packages.config' - feedsToUse: config - nugetConfigPath: 'build\tfs\win32\ESRPClient\NuGet.config' - externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b - restoreDirectory: packages - - - task: ESRPImportCertTask@1 - displayName: Import ESRP Request Signing Certificate - inputs: - ESRP: 'ESRP CodeSign' - - - powershell: | - $ErrorActionPreference = "Stop" - .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) - displayName: Import ESRP Auth Certificate - - - powershell: | - $ErrorActionPreference = "Stop" - npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup" - - $Repo = "$(pwd)" - $Root = "$Repo\.." - $SystemExe = "$Repo\.build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe" - $UserExe = "$Repo\.build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe" - $Zip = "$Repo\.build\win32-$(VSCODE_ARCH)\archive\VSCode-win32-$(VSCODE_ARCH).zip" - $Build = "$Root\VSCode-win32-$(VSCODE_ARCH)" - - # get version - $PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json - $Version = $PackageJson.version - $Quality = "$env:VSCODE_QUALITY" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)" - $env:MOONCAKE_STORAGE_ACCESS_KEY = "$(MOONCAKE_STORAGE_ACCESS_KEY)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)" - - $assetPlatform = if ("$(VSCODE_ARCH)" -eq "ia32") { "win32" } else { "win32-x64" } - - node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip - node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe - node build/tfs/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe - - # publish hockeyapp symbols - $hockeyAppId = if ("$(VSCODE_ARCH)" -eq "ia32") { "$(VSCODE_HOCKEYAPP_ID_WIN32)" } else { "$(VSCODE_HOCKEYAPP_ID_WIN64)" } - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId +phases: +- phase: Windows32 + condition: eq(variables['VSCODE_BUILD_WIN32'], 'true') + queue: Hosted VS2017 + variables: + VSCODE_ARCH: ia32 + steps: + - template: build/tfs/win32/product-build-win32.yml - phase: Linux condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') queue: linux-x64 variables: VSCODE_ARCH: x64 - steps: - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" - - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - - script: | - set -e - export npm_config_arch="$(VSCODE_ARCH)" - if [[ "$(VSCODE_ARCH)" == "ia32" ]]; then - export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" - fi - - echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc - yarn - npm run gulp -- hygiene - npm run monaco-compile-check - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin - node build/tfs/common/installDistro.js - node build/lib/builtInExtensions.js - - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- vscode-linux-$(VSCODE_ARCH)-min - name: build - - - script: | - set -e - npm run gulp -- "electron-$(VSCODE_ARCH)" - DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" - # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)" - name: test - - - script: | - set -e - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" - #npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-snap" - - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - ./build/tfs/linux/release.sh "$(VSCODE_ARCH)" "$(LINUX_REPO_PASSWORD)" - - # publish hockeyapp symbols - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX64)" + - template: build/tfs/linux/product-build-linux.yml - phase: Linux32 condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') queue: linux-ia32 variables: VSCODE_ARCH: ia32 - steps: - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" - - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - - script: | - set -e - export npm_config_arch="$(VSCODE_ARCH)" - if [[ "$(VSCODE_ARCH)" == "ia32" ]]; then - export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" - fi - - echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc - yarn - npm run gulp -- hygiene - npm run monaco-compile-check - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin - node build/tfs/common/installDistro.js - node build/lib/builtInExtensions.js - - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- vscode-linux-$(VSCODE_ARCH)-min - name: build - - - script: | - set -e - npm run gulp -- "electron-$(VSCODE_ARCH)" - DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" - # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)" - name: test - - - script: | - set -e - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" - #npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-snap" - - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - ./build/tfs/linux/release.sh "$(VSCODE_ARCH)" "$(LINUX_REPO_PASSWORD)" - - # publish hockeyapp symbols - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX32)" + - template: build/tfs/linux/product-build-linux.yml - phase: macOS condition: eq(variables['VSCODE_BUILD_MACOS'], 'true') queue: Hosted macOS Preview steps: - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" - - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - - script: | - set -e - echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc - yarn - npm run gulp -- hygiene - npm run monaco-compile-check - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin - node build/tfs/common/installDistro.js - node build/lib/builtInExtensions.js - - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - npm run gulp -- vscode-darwin-min upload-vscode-sourcemaps - name: build - - - script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`" - # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME" - name: test - - - script: | - set -e - # archive the unsigned build - pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin-unsigned.zip * && popd - - # publish the unsigned build - PACKAGEJSON=`ls ../VSCode-darwin/*.app/Contents/Resources/app/package.json` - VERSION=`node -p "require(\"$PACKAGEJSON\").version"` - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/tfs/common/publish.js \ - "$(VSCODE_QUALITY)" \ - darwin \ - archive-unsigned \ - "VSCode-darwin-$(VSCODE_QUALITY)-unsigned.zip" \ - $VERSION \ - false \ - ../VSCode-darwin-unsigned.zip - - # publish hockeyapp symbols - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_MACOS)" - - # enqueue the unsigned build - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - node build/tfs/darwin/enqueue.js "$(VSCODE_QUALITY)" - - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - npm run gulp -- upload-vscode-configuration \ No newline at end of file + - template: build/tfs/darwin/product-build-darwin.yml \ No newline at end of file diff --git a/build/tfs/win32/product-build-win32.yml b/build/tfs/win32/product-build-win32.yml new file mode 100644 index 00000000000..bcf430ce7e7 --- /dev/null +++ b/build/tfs/win32/product-build-win32.yml @@ -0,0 +1,162 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.9.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.3.2" + +- powershell: | + $ErrorActionPreference = "Stop" + "machine monacotools.visualstudio.com password $(VSO_PAT)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + $env:npm_config_arch="$(VSCODE_ARCH)" + $env:CHILD_CONCURRENCY="1" + yarn + npm run gulp -- hygiene + npm run monaco-compile-check + $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" + npm run gulp -- mixin + node build/tfs/common/installDistro.js + node build/lib/builtInExtensions.js + +- powershell: | + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" + npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-min" + npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-copy-inno-updater" + name: build + +- powershell: | + $ErrorActionPreference = "Stop" + npm run gulp -- "electron-$(VSCODE_ARCH)" + .\scripts\test.bat --build --tfs "Unit Tests" + # yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + name: test + +- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: 'ESRP CodeSign' + FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)' + Pattern: '*.dll,*.exe,*.node' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-229803", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "VS Code" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "https://code.visualstudio.com/" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/t \"http://ts4096.gtm.microsoft.com/TSS/AuthenticodeTS\"" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "VS Code" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "https://code.visualstudio.com/" + }, + { + "parameterName": "Append", + "parameterValue": "/as" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolVerify", + "parameters": [ + { + "parameterName": "VerifyAll", + "parameterValue": "/all" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 + +- task: NuGetCommand@2 + displayName: Install ESRPClient.exe + inputs: + restoreSolution: 'build\tfs\win32\ESRPClient\packages.config' + feedsToUse: config + nugetConfigPath: 'build\tfs\win32\ESRPClient\NuGet.config' + externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b + restoreDirectory: packages + +- task: ESRPImportCertTask@1 + displayName: Import ESRP Request Signing Certificate + inputs: + ESRP: 'ESRP CodeSign' + +- powershell: | + $ErrorActionPreference = "Stop" + .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) + displayName: Import ESRP Auth Certificate + +- powershell: | + $ErrorActionPreference = "Stop" + npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup" + + $Repo = "$(pwd)" + $Root = "$Repo\.." + $SystemExe = "$Repo\.build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe" + $UserExe = "$Repo\.build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe" + $Zip = "$Repo\.build\win32-$(VSCODE_ARCH)\archive\VSCode-win32-$(VSCODE_ARCH).zip" + $Build = "$Root\VSCode-win32-$(VSCODE_ARCH)" + + # get version + $PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json + $Version = $PackageJson.version + $Quality = "$env:VSCODE_QUALITY" + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)" + $env:MOONCAKE_STORAGE_ACCESS_KEY = "$(MOONCAKE_STORAGE_ACCESS_KEY)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)" + + $assetPlatform = if ("$(VSCODE_ARCH)" -eq "ia32") { "win32" } else { "win32-x64" } + + node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip + node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe + node build/tfs/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe + + # publish hockeyapp symbols + $hockeyAppId = if ("$(VSCODE_ARCH)" -eq "ia32") { "$(VSCODE_HOCKEYAPP_ID_WIN32)" } else { "$(VSCODE_HOCKEYAPP_ID_WIN64)" } + node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId From 87ccaa989aed96c35f5d9284063a40aaf06baaab Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 12:53:52 +0200 Subject: [PATCH 085/151] use 32 bit env variables --- build/tfs/product-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index f76b9c42a49..2c678309cc6 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -9,7 +9,7 @@ phases: phases: - phase: Windows32 - condition: eq(variables['VSCODE_BUILD_WIN32'], 'true') + condition: eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true') queue: Hosted VS2017 variables: VSCODE_ARCH: ia32 @@ -25,7 +25,7 @@ phases: - template: build/tfs/linux/product-build-linux.yml - phase: Linux32 - condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + condition: eq(variables['VSCODE_BUILD_LINUX_32BIT'], 'true') queue: linux-ia32 variables: VSCODE_ARCH: ia32 From 123509f05e62ebc676369a43976e7d6f6f532c37 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 12:56:23 +0200 Subject: [PATCH 086/151] use yml templates for continuous build --- build/tfs/continuous-build.yml | 140 +------------------ build/tfs/darwin/continuous-build-darwin.yml | 48 +++++++ build/tfs/linux/continuous-build-linux.yml | 44 ++++++ build/tfs/win32/continuous-build-win32.yml | 48 +++++++ 4 files changed, 143 insertions(+), 137 deletions(-) create mode 100644 build/tfs/darwin/continuous-build-darwin.yml create mode 100644 build/tfs/linux/continuous-build-linux.yml create mode 100644 build/tfs/win32/continuous-build-win32.yml diff --git a/build/tfs/continuous-build.yml b/build/tfs/continuous-build.yml index 79d65b7de84..717be7f9897 100644 --- a/build/tfs/continuous-build.yml +++ b/build/tfs/continuous-build.yml @@ -2,148 +2,14 @@ phases: - phase: Windows queue: Hosted VS2017 steps: - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - powershell: | - yarn - displayName: Install Dependencies - - powershell: | - yarn gulp electron - displayName: Download Electron - - powershell: | - yarn gulp hygiene - displayName: Run Hygiene Checks - - powershell: | - yarn check-monaco-editor-compilation - displayName: Run Monaco Editor Checks - - powershell: | - yarn compile - displayName: Compile Sources - - powershell: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions - - powershell: | - .\scripts\test.bat --tfs "Unit Tests" - displayName: Run Unit Tests - - powershell: | - .\scripts\test-integration.bat --tfs "Integration Tests" - displayName: Run Integration Tests - - powershell: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" - displayName: Run Smoke Tests - continueOnError: true - - task: PublishBuildArtifacts@1 - displayName: Publish Smoketest Artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: build-artifacts-win32 - publishLocation: Container - condition: eq(variables['System.PullRequest.IsFork'], 'False') - - task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - template: build/tfs/win32/continuous-build-win32.yml - phase: Linux queue: Hosted Linux Preview steps: - - script: | - set -e - apt-get update - apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 libgconf-2-4 dbus xvfb libgtk-3-0 - cp build/tfs/linux/x64/xvfb.init /etc/init.d/xvfb - chmod +x /etc/init.d/xvfb - update-rc.d xvfb defaults - ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon - service xvfb start - service dbus start - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - script: | - yarn - displayName: Install Dependencies - - script: | - yarn gulp electron-x64 - displayName: Download Electron - - script: | - yarn gulp hygiene - displayName: Run Hygiene Checks - - script: | - yarn check-monaco-editor-compilation - displayName: Run Monaco Editor Checks - - script: | - yarn compile - displayName: Compile Sources - - script: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions - - script: | - DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests - - task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - template: build/tfs/linux/continuous-build-linux.yml - phase: macOS queue: Hosted macOS Preview steps: - - task: NodeTool@0 - inputs: - versionSpec: "8.9.1" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.3.2" - - script: | - yarn - displayName: Install Dependencies - - script: | - yarn gulp electron-x64 - displayName: Download Electron - - script: | - yarn gulp hygiene - displayName: Run Hygiene Checks - - script: | - yarn check-monaco-editor-compilation - displayName: Run Monaco Editor Checks - - script: | - yarn compile - displayName: Compile Sources - - script: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions - - script: | - ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests - - script: | - ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests - - script: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)/artifacts" --log "$(Build.ArtifactStagingDirectory)/artifacts/smoketest.log" - displayName: Run Smoke Tests - continueOnError: true - - task: PublishBuildArtifacts@1 - displayName: Publish Smoketest Artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: build-artifacts-darwin - publishLocation: Container - condition: eq(variables['System.PullRequest.IsFork'], 'False') - - task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() \ No newline at end of file + - template: build/tfs/darwin/continuous-build-darwin.yml \ No newline at end of file diff --git a/build/tfs/darwin/continuous-build-darwin.yml b/build/tfs/darwin/continuous-build-darwin.yml new file mode 100644 index 00000000000..20070e08745 --- /dev/null +++ b/build/tfs/darwin/continuous-build-darwin.yml @@ -0,0 +1,48 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.9.1" +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.3.2" +- script: | + yarn + displayName: Install Dependencies +- script: | + yarn gulp electron-x64 + displayName: Download Electron +- script: | + yarn gulp hygiene + displayName: Run Hygiene Checks +- script: | + yarn check-monaco-editor-compilation + displayName: Run Monaco Editor Checks +- script: | + yarn compile + displayName: Compile Sources +- script: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions +- script: | + ./scripts/test.sh --tfs "Unit Tests" + displayName: Run Unit Tests +- script: | + ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run Integration Tests +- script: | + yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)/artifacts" --log "$(Build.ArtifactStagingDirectory)/artifacts/smoketest.log" + displayName: Run Smoke Tests + continueOnError: true +- task: PublishBuildArtifacts@1 + displayName: Publish Smoketest Artifacts + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + ArtifactName: build-artifacts-darwin + publishLocation: Container + condition: eq(variables['System.PullRequest.IsFork'], 'False') +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() \ No newline at end of file diff --git a/build/tfs/linux/continuous-build-linux.yml b/build/tfs/linux/continuous-build-linux.yml new file mode 100644 index 00000000000..7ec3aec74b9 --- /dev/null +++ b/build/tfs/linux/continuous-build-linux.yml @@ -0,0 +1,44 @@ +steps: +- script: | + set -e + apt-get update + apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 libgconf-2-4 dbus xvfb libgtk-3-0 + cp build/tfs/linux/x64/xvfb.init /etc/init.d/xvfb + chmod +x /etc/init.d/xvfb + update-rc.d xvfb defaults + ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon + service xvfb start + service dbus start +- task: NodeTool@0 + inputs: + versionSpec: "8.9.1" +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.3.2" +- script: | + yarn + displayName: Install Dependencies +- script: | + yarn gulp electron-x64 + displayName: Download Electron +- script: | + yarn gulp hygiene + displayName: Run Hygiene Checks +- script: | + yarn check-monaco-editor-compilation + displayName: Run Monaco Editor Checks +- script: | + yarn compile + displayName: Compile Sources +- script: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions +- script: | + DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" + displayName: Run Unit Tests +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() \ No newline at end of file diff --git a/build/tfs/win32/continuous-build-win32.yml b/build/tfs/win32/continuous-build-win32.yml new file mode 100644 index 00000000000..cfa8712c5db --- /dev/null +++ b/build/tfs/win32/continuous-build-win32.yml @@ -0,0 +1,48 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.9.1" +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.3.2" +- powershell: | + yarn + displayName: Install Dependencies +- powershell: | + yarn gulp electron + displayName: Download Electron +- powershell: | + yarn gulp hygiene + displayName: Run Hygiene Checks +- powershell: | + yarn check-monaco-editor-compilation + displayName: Run Monaco Editor Checks +- powershell: | + yarn compile + displayName: Compile Sources +- powershell: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions +- powershell: | + .\scripts\test.bat --tfs "Unit Tests" + displayName: Run Unit Tests +- powershell: | + .\scripts\test-integration.bat --tfs "Integration Tests" + displayName: Run Integration Tests +- powershell: | + yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" + displayName: Run Smoke Tests + continueOnError: true +- task: PublishBuildArtifacts@1 + displayName: Publish Smoketest Artifacts + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + ArtifactName: build-artifacts-win32 + publishLocation: Container + condition: eq(variables['System.PullRequest.IsFork'], 'False') +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() From 99dc1c2fe5acf9f6af2a4f3e3672072761cdad1b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 12:59:08 +0200 Subject: [PATCH 087/151] fix build paths --- build/tfs/continuous-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/tfs/continuous-build.yml b/build/tfs/continuous-build.yml index 717be7f9897..a2cfee54013 100644 --- a/build/tfs/continuous-build.yml +++ b/build/tfs/continuous-build.yml @@ -2,14 +2,14 @@ phases: - phase: Windows queue: Hosted VS2017 steps: - - template: build/tfs/win32/continuous-build-win32.yml + - template: win32/continuous-build-win32.yml - phase: Linux queue: Hosted Linux Preview steps: - - template: build/tfs/linux/continuous-build-linux.yml + - template: linux/continuous-build-linux.yml - phase: macOS queue: Hosted macOS Preview steps: - - template: build/tfs/darwin/continuous-build-darwin.yml \ No newline at end of file + - template: darwin/continuous-build-darwin.yml \ No newline at end of file From 55ab81ff6503231279246267ea9ac15e52c0375f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 13:00:10 +0200 Subject: [PATCH 088/151] update path --- build/tfs/product-build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index 2c678309cc6..091820ff546 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -5,7 +5,7 @@ phases: variables: VSCODE_ARCH: ia32 steps: - - template: build/tfs/win32/product-build-win32.yml + - template: win32/product-build-win32.yml phases: - phase: Windows32 @@ -14,7 +14,7 @@ phases: variables: VSCODE_ARCH: ia32 steps: - - template: build/tfs/win32/product-build-win32.yml + - template: win32/product-build-win32.yml - phase: Linux condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') @@ -22,7 +22,7 @@ phases: variables: VSCODE_ARCH: x64 steps: - - template: build/tfs/linux/product-build-linux.yml + - template: linux/product-build-linux.yml - phase: Linux32 condition: eq(variables['VSCODE_BUILD_LINUX_32BIT'], 'true') @@ -30,10 +30,10 @@ phases: variables: VSCODE_ARCH: ia32 steps: - - template: build/tfs/linux/product-build-linux.yml + - template: linux/product-build-linux.yml - phase: macOS condition: eq(variables['VSCODE_BUILD_MACOS'], 'true') queue: Hosted macOS Preview steps: - - template: build/tfs/darwin/product-build-darwin.yml \ No newline at end of file + - template: darwin/product-build-darwin.yml \ No newline at end of file From cfc88838a37b237b10c03488e6f3a13b1538c467 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 13:00:51 +0200 Subject: [PATCH 089/151] fix product build --- build/tfs/product-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index 091820ff546..2f2dce5b94c 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -7,7 +7,6 @@ phases: steps: - template: win32/product-build-win32.yml -phases: - phase: Windows32 condition: eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true') queue: Hosted VS2017 From 160f3ba08a51907652edb887372ac8245d1fca99 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 13:03:22 +0200 Subject: [PATCH 090/151] use exec in win32 continuous build related to #52805 --- build/tfs/win32/continuous-build-win32.yml | 27 ++++++++++++++-------- build/tfs/win32/exec.ps1 | 24 +++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 build/tfs/win32/exec.ps1 diff --git a/build/tfs/win32/continuous-build-win32.yml b/build/tfs/win32/continuous-build-win32.yml index cfa8712c5db..ffb4668eff2 100644 --- a/build/tfs/win32/continuous-build-win32.yml +++ b/build/tfs/win32/continuous-build-win32.yml @@ -6,31 +6,40 @@ steps: inputs: versionSpec: "1.3.2" - powershell: | - yarn + . build/tfs/win32/exec.ps1 + exec { yarn } displayName: Install Dependencies - powershell: | - yarn gulp electron + . build/tfs/win32/exec.ps1 + exec { yarn gulp electron } displayName: Download Electron - powershell: | - yarn gulp hygiene + . build/tfs/win32/exec.ps1 + exec { yarn gulp hygiene } displayName: Run Hygiene Checks - powershell: | - yarn check-monaco-editor-compilation + . build/tfs/win32/exec.ps1 + exec { yarn check-monaco-editor-compilation } displayName: Run Monaco Editor Checks - powershell: | - yarn compile + . build/tfs/win32/exec.ps1 + exec { yarn compile } displayName: Compile Sources - powershell: | - yarn download-builtin-extensions + . build/tfs/win32/exec.ps1 + exec { yarn download-builtin-extensions } displayName: Download Built-in Extensions - powershell: | - .\scripts\test.bat --tfs "Unit Tests" + . build/tfs/win32/exec.ps1 + exec { .\scripts\test.bat --tfs "Unit Tests" } displayName: Run Unit Tests - powershell: | - .\scripts\test-integration.bat --tfs "Integration Tests" + . build/tfs/win32/exec.ps1 + exec { .\scripts\test-integration.bat --tfs "Integration Tests" } displayName: Run Integration Tests - powershell: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" + . build/tfs/win32/exec.ps1 + exec { yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" } displayName: Run Smoke Tests continueOnError: true - task: PublishBuildArtifacts@1 diff --git a/build/tfs/win32/exec.ps1 b/build/tfs/win32/exec.ps1 new file mode 100644 index 00000000000..826cefdf7dd --- /dev/null +++ b/build/tfs/win32/exec.ps1 @@ -0,0 +1,24 @@ +# Taken from psake https://github.com/psake/psake + +<# +.SYNOPSIS + This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode + to see if an error occcured. If an error is detected then an exception is thrown. + This function allows you to run command-line programs without having to + explicitly check the $lastexitcode variable. + +.EXAMPLE + exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed" +#> +function Exec +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, + [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd) + ) + & $cmd + if ($lastexitcode -ne 0) { + throw ("Exec: " + $errorMessage) + } +} \ No newline at end of file From 778a9bafbbf49a9fb76e09e00a1678a7687ad555 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 13:12:42 +0200 Subject: [PATCH 091/151] more windows build error catching related to #52805 --- build/tfs/win32/continuous-build-win32.yml | 9 ++++++ build/tfs/win32/product-build-win32.yml | 37 ++++++++++++---------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/build/tfs/win32/continuous-build-win32.yml b/build/tfs/win32/continuous-build-win32.yml index ffb4668eff2..6490c637b78 100644 --- a/build/tfs/win32/continuous-build-win32.yml +++ b/build/tfs/win32/continuous-build-win32.yml @@ -7,38 +7,47 @@ steps: versionSpec: "1.3.2" - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn } displayName: Install Dependencies - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn gulp electron } displayName: Download Electron - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn gulp hygiene } displayName: Run Hygiene Checks - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn check-monaco-editor-compilation } displayName: Run Monaco Editor Checks - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn compile } displayName: Compile Sources - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn download-builtin-extensions } displayName: Download Built-in Extensions - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { .\scripts\test.bat --tfs "Unit Tests" } displayName: Run Unit Tests - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { .\scripts\test-integration.bat --tfs "Integration Tests" } displayName: Run Integration Tests - powershell: | . build/tfs/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" } displayName: Run Smoke Tests continueOnError: true diff --git a/build/tfs/win32/product-build-win32.yml b/build/tfs/win32/product-build-win32.yml index bcf430ce7e7..20546a2f5fc 100644 --- a/build/tfs/win32/product-build-win32.yml +++ b/build/tfs/win32/product-build-win32.yml @@ -8,29 +8,32 @@ steps: versionSpec: "1.3.2" - powershell: | + . build/tfs/win32/exec.ps1 $ErrorActionPreference = "Stop" "machine monacotools.visualstudio.com password $(VSO_PAT)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII $env:npm_config_arch="$(VSCODE_ARCH)" $env:CHILD_CONCURRENCY="1" - yarn - npm run gulp -- hygiene - npm run monaco-compile-check $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" - npm run gulp -- mixin - node build/tfs/common/installDistro.js - node build/lib/builtInExtensions.js + exec { yarn } + exec { npm run gulp -- hygiene } + exec { npm run monaco-compile-check } + exec { npm run gulp -- mixin } + exec { node build/tfs/common/installDistro.js } + exec { node build/lib/builtInExtensions.js } - powershell: | + . build/tfs/win32/exec.ps1 $ErrorActionPreference = "Stop" $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" - npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-min" - npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-copy-inno-updater" + exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-min" } + exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-copy-inno-updater" } name: build - powershell: | + . build/tfs/win32/exec.ps1 $ErrorActionPreference = "Stop" - npm run gulp -- "electron-$(VSCODE_ARCH)" - .\scripts\test.bat --build --tfs "Unit Tests" + exec { npm run gulp -- "electron-$(VSCODE_ARCH)" } + exec { .\scripts\test.bat --build --tfs "Unit Tests" } # yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" name: test @@ -128,13 +131,15 @@ steps: ESRP: 'ESRP CodeSign' - powershell: | + . build/tfs/win32/exec.ps1 $ErrorActionPreference = "Stop" - .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) + exec { .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) } displayName: Import ESRP Auth Certificate - powershell: | + . build/tfs/win32/exec.ps1 $ErrorActionPreference = "Stop" - npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup" + exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup" } $Repo = "$(pwd)" $Root = "$Repo\.." @@ -153,10 +158,10 @@ steps: $assetPlatform = if ("$(VSCODE_ARCH)" -eq "ia32") { "win32" } else { "win32-x64" } - node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip - node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe - node build/tfs/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe + exec { node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip } + exec { node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe } + exec { node build/tfs/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe } # publish hockeyapp symbols $hockeyAppId = if ("$(VSCODE_ARCH)" -eq "ia32") { "$(VSCODE_HOCKEYAPP_ID_WIN32)" } else { "$(VSCODE_HOCKEYAPP_ID_WIN64)" } - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId + exec { node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId } From ea60a696d708a49094f0379c698641826f4e4041 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 10 Jul 2018 15:39:28 +0200 Subject: [PATCH 092/151] Update out-of-scope comment, add /needsMoreInfo --- .github/commands.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/commands.yml b/.github/commands.yml index ba8c8d7f3ce..56edba1d4da 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -29,7 +29,7 @@ type: 'label', name: '*out-of-scope', action: 'close', - comment: "This issue is being closed to keep the number of issues in our inbox on a manageable level, we are closing issues that have been on the backlog for a long time but have not gained traction: We look at the number of votes the issue has received and the number of duplicate issues filed. If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!" + comment: "This issue is being closed to keep the number of issues in our inbox on a manageable level, we are closing issues that are not going to be addressed in the foreseeable future: We look at the number of votes the issue has received and the number of duplicate issues filed. If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!" }, { type: 'label', @@ -62,5 +62,12 @@ action: 'comment', comment: "Potential duplicates:\n${potentialDuplicates}" }, + { + type: 'comment', + name: 'needsMoreInfo', + action: 'updateLabels', + addLabel: 'needs more info', + comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" + }, ] } From e5b1f6f31bc097e674d8a23374ad4e9c3771bb3a Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 10 Jul 2018 15:51:39 +0200 Subject: [PATCH 093/151] Fixes #53960: Allow task without task definitions. Treat all properties as mandantroy. --- src/vs/workbench/api/node/extHostTask.ts | 3 ++- src/vs/workbench/parts/tasks/node/tasks.ts | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index c8a40bb16a8..ee825674099 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -871,7 +871,8 @@ export class ExtHostTask implements ExtHostTaskShape { if (task.definition && validTypes[task.definition.type] === true) { sanitized.push(task); } else { - console.error(`Dropping task [${task.source}, ${task.name}]. Its type is not known to the system.`); + sanitized.push(task); + console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`); } } let workspaceFolders = this._workspaceService.getWorkspaceFolders(); diff --git a/src/vs/workbench/parts/tasks/node/tasks.ts b/src/vs/workbench/parts/tasks/node/tasks.ts index c0cd15ad06d..a24e94c013a 100644 --- a/src/vs/workbench/parts/tasks/node/tasks.ts +++ b/src/vs/workbench/parts/tasks/node/tasks.ts @@ -27,7 +27,10 @@ namespace TaskDefinition { export function createTaskIdentifier(external: TaskIdentifier, reporter: { error(message: string): void; }): KeyedTaskIdentifier | undefined { let definition = TaskDefinitionRegistry.get(external.type); if (definition === void 0) { - return undefined; + // We have no task definition so we can't sanitize the literal. Take it as is + let copy = Objects.deepClone(external); + delete copy._key; + return KeyedTaskIdentifier.create(copy); } let literal: { type: string;[name: string]: any } = Object.create(null); From 6d7f8145310d70ce0178be9740aeb47c5355803d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 10 Jul 2018 16:25:21 +0200 Subject: [PATCH 094/151] [explorer] new-file input-box shows icon of parent folder. Fixes #53655 --- .../files/electron-browser/views/explorerViewer.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index 09615b2bae6..1c5deb3215b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -275,7 +275,11 @@ export class FileRenderer implements IRenderer { const extraClasses = ['explorer-item', 'explorer-item-edited']; const fileKind = stat.isRoot ? FileKind.ROOT_FOLDER : (stat.isDirectory || (stat instanceof NewStatPlaceholder && stat.isDirectoryPlaceholder())) ? FileKind.FOLDER : FileKind.FILE; const labelOptions: IFileLabelOptions = { hidePath: true, hideLabel: true, fileKind, extraClasses }; - label.setFile(stat.resource, labelOptions); + + const parent = stat.name ? resources.dirname(stat.resource) : stat.resource; + const value = stat.name || ''; + + label.setFile(parent.with({ path: paths.join(parent.path, value || ' ') }), labelOptions); // Use icon for ' ' if name is empty. // Input field for name const inputBox = new InputBox(label.element, this.contextViewService, { @@ -286,12 +290,10 @@ export class FileRenderer implements IRenderer { }); const styler = attachInputBoxStyler(inputBox, this.themeService); - const parent = resources.dirname(stat.resource); inputBox.onDidChange(value => { - label.setFile(parent.with({ path: paths.join(parent.path, value) }), labelOptions); // update label icon while typing! + label.setFile(parent.with({ path: paths.join(parent.path, value || ' ') }), labelOptions); // update label icon while typing! }); - const value = stat.name || ''; const lastDot = value.lastIndexOf('.'); inputBox.value = value; From 8f16f8c8bae109488baa5f17aeb97756b3bfa21b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Jul 2018 17:09:21 +0200 Subject: [PATCH 095/151] list - list.clear only when there is a selection or focus (for #53950) --- src/vs/platform/list/browser/listService.ts | 24 ++++++++++++++++++ src/vs/workbench/electron-browser/commands.ts | 25 ++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index bc246f6ed0c..0a6b5ed6263 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -90,6 +90,7 @@ export class ListService implements IListService { const RawWorkbenchListFocusContextKey = new RawContextKey('listFocus', true); export const WorkbenchListSupportsMultiSelectContextKey = new RawContextKey('listSupportsMultiselect', true); export const WorkbenchListFocusContextKey = ContextKeyExpr.and(RawWorkbenchListFocusContextKey, ContextKeyExpr.not(InputFocusedContextKey)); +export const WorkbenchListHasSelectionOrFocus = new RawContextKey('listHasSelectionOrFocus', false); export const WorkbenchListDoubleSelection = new RawContextKey('listDoubleSelection', false); export const WorkbenchListMultiSelection = new RawContextKey('listMultiSelection', false); @@ -199,6 +200,7 @@ export class WorkbenchList extends List { readonly contextKeyService: IContextKeyService; + private listHasSelectionOrFocus: IContextKey; private listDoubleSelection: IContextKey; private listMultiSelection: IContextKey; @@ -225,6 +227,7 @@ export class WorkbenchList extends List { ); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + this.listHasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.listMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); @@ -236,8 +239,17 @@ export class WorkbenchList extends List { attachListStyler(this, themeService), this.onSelectionChange(() => { const selection = this.getSelection(); + const focus = this.getFocus(); + + this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); this.listMultiSelection.set(selection.length > 1); this.listDoubleSelection.set(selection.length === 2); + }), + this.onFocusChange(() => { + const selection = this.getSelection(); + const focus = this.getFocus(); + + this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); }) ])); @@ -323,6 +335,7 @@ export class WorkbenchTree extends Tree { protected disposables: IDisposable[]; + private listHasSelectionOrFocus: IContextKey; private listDoubleSelection: IContextKey; private listMultiSelection: IContextKey; @@ -352,6 +365,7 @@ export class WorkbenchTree extends Tree { this.disposables = []; this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + this.listHasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.listMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); @@ -366,10 +380,20 @@ export class WorkbenchTree extends Tree { this.disposables.push(this.onDidChangeSelection(() => { const selection = this.getSelection(); + const focus = this.getFocus(); + + this.listHasSelectionOrFocus.set((selection && selection.length > 0) || !!focus); this.listDoubleSelection.set(selection && selection.length === 2); this.listMultiSelection.set(selection && selection.length > 1); })); + this.disposables.push(this.onDidChangeFocus(() => { + const selection = this.getSelection(); + const focus = this.getFocus(); + + this.listHasSelectionOrFocus.set((selection && selection.length > 0) || !!focus); + })); + this.disposables.push(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(openModeSettingKey)) { this._openOnSingleClick = useSingleClickToOpen(configurationService); diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index ecb6e315f43..63fce9d272b 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -13,7 +13,7 @@ import { IWindowsService, IWindowService } from 'vs/platform/windows/common/wind import { List } from 'vs/base/browser/ui/list/listWidget'; import * as errors from 'vs/base/common/errors'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget } from 'vs/platform/list/browser/listService'; +import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus } from 'vs/platform/list/browser/listService'; import { PagedList } from 'vs/base/browser/ui/list/listPaging'; import { range } from 'vs/base/common/arrays'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -471,13 +471,30 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.clear', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), - when: WorkbenchListFocusContextKey, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListHasSelectionOrFocus), primary: KeyCode.Escape, handler: (accessor) => { const focused = accessor.get(IListService).lastFocusedList; - // Tree only - if (focused && !(focused instanceof List || focused instanceof PagedList)) { + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + if (list.getSelection().length > 0) { + list.setSelection([]); + + return void 0; + } + + if (list.getFocus().length > 0) { + list.setFocus([]); + + return void 0; + } + } + + // Tree + else if (focused) { const tree = focused; if (tree.getSelection().length) { From 5fd4be91c6a4508bfbadbbea917cb117115d4a4b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 17:41:31 +0200 Subject: [PATCH 096/151] tree: node.collapsible --- src/vs/base/browser/ui/tree/tree.ts | 12 ++++++++++-- src/vs/base/browser/ui/tree/treeModel.ts | 12 ++++++++++-- test/tree/server.js | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 6cd72d3311f..a6546cf4830 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -63,6 +63,14 @@ interface ITreeListTemplateData { templateData: T; } +function renderTwistie(node: ITreeNode, twistie: HTMLElement): void { + if (node.children.length === 0 && !node.collapsible) { + twistie.innerText = ''; + } else { + twistie.innerText = node.collapsed ? '▹' : '◢'; + } +} + class TreeRenderer implements IRenderer, ITreeListTemplateData> { readonly templateId: string; @@ -92,8 +100,8 @@ class TreeRenderer implements IRenderer, ITreeLis this.renderedNodes.set(node, templateData); templateData.elementDisposable = toDisposable(() => this.renderedNodes.delete(node)); - templateData.twistie.innerText = node.children.length === 0 ? '' : (node.collapsed ? '▹' : '◢'); templateData.twistie.style.width = `${10 + node.depth * 10}px`; + renderTwistie(node, templateData.twistie); this.renderer.renderElement(node.element, index, templateData.templateData); } @@ -109,7 +117,7 @@ class TreeRenderer implements IRenderer, ITreeLis return; } - templateData.twistie.innerText = node.children.length === 0 ? '' : (node.collapsed ? '▹' : '◢'); + renderTwistie(node, templateData.twistie); } dispose(): void { diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index 8d3c14b727b..fb063950e37 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -12,6 +12,7 @@ import { Emitter, Event } from 'vs/base/common/event'; export interface ITreeElement { readonly element: T; readonly children?: IIterator> | ITreeElement[]; + readonly collapsible?: boolean; readonly collapsed?: boolean; } @@ -20,6 +21,7 @@ export interface ITreeNode { readonly element: T; readonly children: IMutableTreeNode[]; readonly depth: number; + readonly collapsible: boolean; readonly collapsed: boolean; readonly visibleCount: number; } @@ -61,8 +63,8 @@ function getTreeElementIterator(elements: IIterator> | ITreeE function treeElementToNode(treeElement: ITreeElement, parent: IMutableTreeNode, visible: boolean, treeListElements: ITreeNode[]): IMutableTreeNode { const depth = parent.depth + 1; - const { element, collapsed } = treeElement; - const node = { parent, element, children: [], depth, collapsed: !!collapsed, visibleCount: 0 }; + const { element, collapsible, collapsed } = treeElement; + const node = { parent, element, children: [], depth, collapsible: !!collapsible, collapsed: !!collapsed, visibleCount: 0 }; if (visible) { treeListElements.push(node); @@ -70,6 +72,7 @@ function treeElementToNode(treeElement: ITreeElement, parent: IMutableTree const children = getTreeElementIterator(treeElement.children); node.children = collect(map(children, el => treeElementToNode(el, node, visible && !treeElement.collapsed, treeListElements))); + node.collapsible = node.collapsible || node.children.length > 0; node.visibleCount = 1 + getVisibleCount(node.children); return node; @@ -89,6 +92,7 @@ export class TreeModel { element: undefined, children: [], depth: 0, + collapsible: false, collapsed: false, visibleCount: 1 }; @@ -131,6 +135,10 @@ export class TreeModel { private _setCollapsed(location: number[], collapsed?: boolean | undefined): boolean { const { node, listIndex, visible } = this.findNode(location); + if (!node.collapsible) { + return false; + } + if (typeof collapsed === 'undefined') { collapsed = !node.collapsed; } diff --git a/test/tree/server.js b/test/tree/server.js index 2a931b51727..b1105bfb741 100644 --- a/test/tree/server.js +++ b/test/tree/server.js @@ -23,7 +23,7 @@ async function getTree(fsPath, level) { const childNames = await fs.readdir(fsPath); const children = await Promise.all(childNames.map(async childName => await getTree(path.join(fsPath, childName), level + 1))); - return { element, collapsed: false, children }; + return { element, collapsible: true, collapsed: false, children }; } app.use(serve('public')); From a9c579bd0d978142afcff4320556472e0196a634 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 17:51:53 +0200 Subject: [PATCH 097/151] tree: left/right arrow navigation --- src/vs/base/browser/ui/tree/tree.ts | 46 +++++++++++++----------- src/vs/base/browser/ui/tree/treeModel.ts | 15 ++++++++ 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index a6546cf4830..7870f58dd1d 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -6,13 +6,14 @@ import 'vs/css!./tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IIdentityProvider, IMultipleSelectionController } from 'vs/base/browser/ui/list/listWidget'; -import { TreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/treeModel'; +import { TreeModel, ITreeNode, ITreeElement, getNodeLocation } from 'vs/base/browser/ui/tree/treeModel'; import { IIterator, empty } from 'vs/base/common/iterator'; import { IDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { append, $ } from 'vs/base/browser/dom'; import { Event, Relay, chain } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { tail2 } from 'vs/base/common/arrays'; function toTreeListOptions(options?: IListOptions): IListOptions> { if (!options) { @@ -126,17 +127,6 @@ class TreeRenderer implements IRenderer, ITreeLis } } -function getLocation(node: ITreeNode): number[] { - const location = []; - - while (node.parent) { - location.push(node.parent.children.indexOf(node)); - node = node.parent; - } - - return location.reverse(); -} - function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } @@ -182,7 +172,7 @@ export class Tree implements IDisposable { private onMouseClick(e: IListMouseEvent>): void { const node = e.element; - const location = getLocation(node); + const location = getNodeLocation(node); this.model.toggleCollapsed(location); } @@ -198,12 +188,17 @@ export class Tree implements IDisposable { } const node = nodes[0]; - const location = getLocation(node); - const didCollapse = this.model.setCollapsed(location, true); + const location = getNodeLocation(node); + const didChange = this.model.setCollapsed(location, true); - if (!didCollapse) { - console.log('should focus parent'); - // this.view.setFocus([]); + if (!didChange) { + if (location.length === 1) { + return; + } + + const [parentLocation] = tail2(location); + const parentListIndex = this.model.getListIndex(parentLocation); + this.view.setFocus([parentListIndex]); } } @@ -218,8 +213,17 @@ export class Tree implements IDisposable { } const node = nodes[0]; - const location = getLocation(node); - this.model.setCollapsed(location, false); + const location = getNodeLocation(node); + const didChange = this.model.setCollapsed(location, false); + + if (!didChange) { + if (node.children.length === 0) { + return; + } + + const [focusedIndex] = this.view.getFocus(); + this.view.setFocus([focusedIndex + 1]); + } } private onSpace(e: StandardKeyboardEvent): void { @@ -233,7 +237,7 @@ export class Tree implements IDisposable { } const node = nodes[0]; - const location = getLocation(node); + const location = getNodeLocation(node); this.model.toggleCollapsed(location); } diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index fb063950e37..34b1add5386 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -85,6 +85,17 @@ function treeNodeToElement(node: IMutableTreeNode): ITreeElement { return { element, children, collapsed }; } +export function getNodeLocation(node: ITreeNode): number[] { + const location = []; + + while (node.parent) { + location.push(node.parent.children.indexOf(node)); + node = node.parent; + } + + return location.reverse(); +} + export class TreeModel { private root: IMutableTreeNode = { @@ -124,6 +135,10 @@ export class TreeModel { return map(iter(deletedNodes), treeNodeToElement); } + getListIndex(location: number[]): number { + return this.findNode(location).listIndex; + } + setCollapsed(location: number[], collapsed: boolean): boolean { return this._setCollapsed(location, collapsed); } From 831da61a7a7b296115b7bbb7dc2d75534ead7ec4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 18:04:09 +0200 Subject: [PATCH 098/151] list: optional renderer.disposeElement --- src/vs/base/browser/ui/list/list.ts | 1 + src/vs/base/browser/ui/list/listView.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index df2fbfde7d3..0042f563078 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -14,6 +14,7 @@ export interface IRenderer { templateId: string; renderTemplate(container: HTMLElement): TTemplateData; renderElement(element: TElement, index: number, templateData: TTemplateData): void; + disposeElement?(element: TElement, index: number, templateData: TTemplateData): void; disposeTemplate(templateData: TTemplateData): void; } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index bbfe7f0b134..2ff18c97783 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -322,6 +322,12 @@ export class ListView implements ISpliceable, IDisposable { throw new Error(`Got index ${index} and there are ${this.items.length} items. File issue to joao!`); } + const renderer = this.renderers.get(item.templateId); + + if (renderer.disposeElement) { + renderer.disposeElement(item.element, index, item.row.templateData); + } + this.cache.release(item.row); item.row = null; } From 044d192df0de6368e53834fca295a4500a4a150e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 18:15:09 +0200 Subject: [PATCH 099/151] remove bad timeout --- extensions/git/src/main.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 9b1d988c842..6eae1da2c9d 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -81,8 +81,6 @@ export async function activate(context: ExtensionContext): Promise { const telemetryReporter = new TelemetryReporter(name, version, aiKey); deactivateTasks.push(() => telemetryReporter.dispose()); - await new Promise(c => setTimeout(c, 10000)); - const config = workspace.getConfiguration('git', null); const enabled = config.get('enabled'); From 5926943418785e693c956c0f1f93fdee47dc6f40 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 18:17:47 +0200 Subject: [PATCH 100/151] list: disposeElement --- src/vs/base/browser/ui/list/list.ts | 2 +- src/vs/base/browser/ui/list/listPaging.ts | 4 ++++ src/vs/base/browser/ui/list/listWidget.ts | 12 ++++++++++++ .../base/browser/ui/selectBox/selectBoxCustom.ts | 4 ++++ src/vs/base/browser/ui/tree/tree.ts | 12 ++++++------ src/vs/base/browser/ui/tree/treeModel.ts | 4 ++-- .../base/test/browser/ui/list/listView.test.ts | 1 + src/vs/editor/contrib/suggest/suggestWidget.ts | 3 +++ .../parts/notifications/notificationsViewer.ts | 4 ++++ .../browser/parts/quickinput/quickInputList.ts | 4 ++++ .../parts/debug/browser/breakpointsView.ts | 16 ++++++++++++++++ .../electron-browser/extensionsList.ts | 4 ++++ .../electron-browser/runtimeExtensionsEditor.ts | 2 ++ .../electron-browser/views/openEditorsView.ts | 8 ++++++++ .../preferences/browser/keybindingsEditor.ts | 5 +++++ .../parts/scm/electron-browser/scmViewlet.ts | 12 ++++++++++++ 16 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 0042f563078..150758f8ae7 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -14,7 +14,7 @@ export interface IRenderer { templateId: string; renderTemplate(container: HTMLElement): TTemplateData; renderElement(element: TElement, index: number, templateData: TTemplateData): void; - disposeElement?(element: TElement, index: number, templateData: TTemplateData): void; + disposeElement(element: TElement, index: number, templateData: TTemplateData): void; disposeTemplate(templateData: TTemplateData): void; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 53ea8be6a37..e5735d6c299 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -50,6 +50,10 @@ class PagedRenderer implements IRenderer this.renderer.renderElement(entry, index, data.data)); } + disposeElement(): void { + // noop + } + disposeTemplate(data: ITemplateData): void { data.disposable.dispose(); data.disposable = null; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index acdcb44f5d8..58c81edc992 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -68,6 +68,10 @@ class TraitRenderer implements IRenderer this.trait.renderIndex(index, templateData); } + disposeElement(): void { + // noop + } + splice(start: number, deleteCount: number, insertCount: number): void { const rendered: IRenderedContainer[] = []; @@ -807,6 +811,14 @@ class PipelineRenderer implements IRenderer { } } + disposeElement(element: T, index: number, templateData: any[]): void { + let i = 0; + + for (const renderer of this.renderers) { + renderer.disposeElement(element, index, templateData[i++]); + } + } + disposeTemplate(templateData: any[]): void { let i = 0; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index fa9bcedd7e3..8185d43aa04 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -67,6 +67,10 @@ class SelectListRenderer implements IRenderer implements IDelegate> { interface ITreeListTemplateData { twistie: HTMLElement; - elementDisposable: IDisposable; templateData: T; } @@ -92,14 +91,11 @@ class TreeRenderer implements IRenderer, ITreeLis const contents = append(el, $('.tl-contents')); const templateData = this.renderer.renderTemplate(contents); - return { twistie, elementDisposable: Disposable.None, templateData }; + return { twistie, templateData }; } renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData): void { - templateData.elementDisposable.dispose(); - this.renderedNodes.set(node, templateData); - templateData.elementDisposable = toDisposable(() => this.renderedNodes.delete(node)); templateData.twistie.style.width = `${10 + node.depth * 10}px`; renderTwistie(node, templateData.twistie); @@ -107,6 +103,10 @@ class TreeRenderer implements IRenderer, ITreeLis this.renderer.renderElement(node.element, index, templateData.templateData); } + disposeElement(node: ITreeNode): void { + this.renderedNodes.delete(node); + } + disposeTemplate(templateData: ITreeListTemplateData): void { this.renderer.disposeTemplate(templateData.templateData); } diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index 34b1add5386..153a413f094 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -165,6 +165,8 @@ export class TreeModel { node.collapsed = collapsed; if (visible) { + this._onDidChangeCollapseState.fire(node); + let visibleCountDiff: number; if (collapsed) { @@ -185,8 +187,6 @@ export class TreeModel { mutableNode.visibleCount += visibleCountDiff; mutableNode = mutableNode.parent; } - - this._onDidChangeCollapseState.fire(node); } return true; diff --git a/src/vs/base/test/browser/ui/list/listView.test.ts b/src/vs/base/test/browser/ui/list/listView.test.ts index 5f4a644c99a..9abc4a41a73 100644 --- a/src/vs/base/test/browser/ui/list/listView.test.ts +++ b/src/vs/base/test/browser/ui/list/listView.test.ts @@ -25,6 +25,7 @@ suite('ListView', function () { templateId: 'template', renderTemplate() { templatesCount++; }, renderElement() { }, + disposeElement() { }, disposeTemplate() { templatesCount--; } }; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index e46dd9613e1..5df91f673f5 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -173,7 +173,10 @@ class Renderer implements IRenderer { data.readMore.onmousedown = null; data.readMore.onclick = null; } + } + disposeElement(): void { + // noop } disposeTemplate(templateData: ISuggestionTemplateData): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 2ecb5ec8da0..8179d53863d 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -278,6 +278,10 @@ export class NotificationRenderer implements IRenderer { }); } + disposeElement(): void { + // noop + } + private updateRecommendationStatus(extension: IExtension, data: ITemplateData) { const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); let ariaLabel = extension.displayName + '. '; diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index ed9c80d3b1a..643a04aa602 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -369,6 +369,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { } }, + disposeElement: () => null, + disposeTemplate: (data: IRuntimeExtensionTemplateData): void => { data.disposables = dispose(data.disposables); } diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index e1b08ea5801..da4cf28b5f0 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -594,6 +594,10 @@ class EditorGroupRenderer implements IRenderer { renderElement(entry: IListEntry, index: number, template: any): void { } + disposeElement(): void { + } + disposeTemplate(template: any): void { } } @@ -704,6 +707,8 @@ class KeybindingItemRenderer implements IRenderer { template.elementDisposable = combinedDisposable(disposables); } + disposeElement(): void { + // noop + } + disposeTemplate(template: ResourceTemplate): void { template.elementDisposable.dispose(); template.dispose(); From 0ac90f822cbee247ea67cea4cfedc73087efce39 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 18:19:16 +0200 Subject: [PATCH 101/151] fix win build --- build/tfs/win32/product-build-win32.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/tfs/win32/product-build-win32.yml b/build/tfs/win32/product-build-win32.yml index 20546a2f5fc..c887ade31fd 100644 --- a/build/tfs/win32/product-build-win32.yml +++ b/build/tfs/win32/product-build-win32.yml @@ -131,9 +131,8 @@ steps: ESRP: 'ESRP CodeSign' - powershell: | - . build/tfs/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) } + .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) displayName: Import ESRP Auth Certificate - powershell: | From 03074c2c2bc319bcdcacfc709b1331650894d97a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 17:16:19 +0200 Subject: [PATCH 102/151] move breadcrumbs into title control, enable different style depending on tabs vs no-tabsfor a start breadcrumbs only show with tabs enabled --- .../browser/parts/editor/breadcrumbs.ts | 47 ++++++ ...orBreadcrumbs.ts => breadcrumbsControl.ts} | 142 +++++++++--------- ...readcrumbsModel.ts => breadcrumbsModel.ts} | 0 .../workbench/browser/parts/editor/editor.ts | 1 + .../browser/parts/editor/editorGroupView.ts | 42 +----- ...{editorbreadcrumbs.css => breadcrumbs.css} | 8 +- .../parts/editor/media/editorgroupview.css | 4 +- .../parts/editor/media/tabstitlecontrol.css | 9 +- .../browser/parts/editor/tabsTitleControl.ts | 53 ++++++- .../browser/parts/editor/titleControl.ts | 10 +- .../group/common/editorGroupsService.ts | 12 -- ...bModel.test.ts => breadcrumbModel.test.ts} | 2 +- .../workbench/test/workbenchTestServices.ts | 4 +- 13 files changed, 200 insertions(+), 134 deletions(-) create mode 100644 src/vs/workbench/browser/parts/editor/breadcrumbs.ts rename src/vs/workbench/browser/parts/editor/{editorBreadcrumbs.ts => breadcrumbsControl.ts} (83%) rename src/vs/workbench/browser/parts/editor/{editorBreadcrumbsModel.ts => breadcrumbsModel.ts} (100%) rename src/vs/workbench/browser/parts/editor/media/{editorbreadcrumbs.css => breadcrumbs.css} (71%) rename src/vs/workbench/test/browser/parts/editor/{editorBreadcrumbModel.test.ts => breadcrumbModel.test.ts} (97%) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts new file mode 100644 index 00000000000..27d5727d3c6 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { GroupIdentifier } from 'vs/workbench/common/editor'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export const IBreadcrumbsService = createDecorator('IEditorBreadcrumbsService'); + +export interface IBreadcrumbsService { + + _serviceBrand: any; + + register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable; + + getWidget(group: GroupIdentifier): BreadcrumbsWidget; +} + + +export class BreadcrumbsService implements IBreadcrumbsService { + + _serviceBrand: any; + + private readonly _map = new Map(); + + register(group: number, widget: BreadcrumbsWidget): IDisposable { + if (this._map.has(group)) { + throw new Error(`group (${group}) has already a widget`); + } + this._map.set(group, widget); + return { + dispose: () => this._map.delete(group) + }; + } + + getWidget(group: number): BreadcrumbsWidget { + return this._map.get(group); + } +} + +registerSingleton(IBreadcrumbsService, BreadcrumbsService); diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts similarity index 83% rename from src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts rename to src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 542a8fb6294..48b68cf6a79 100644 --- a/src/vs/workbench/browser/parts/editor/editorBreadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -16,7 +16,7 @@ import { dirname, isEqual } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; -import 'vs/css!./media/editorbreadcrumbs'; +import 'vs/css!./media/breadcrumbs'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; @@ -35,13 +35,14 @@ import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FileLabel } from 'vs/workbench/browser/labels'; -import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; +import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; -import { EditorInput } from 'vs/workbench/common/editor'; +import { IEditorInput } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; class Item extends BreadcrumbsItem { @@ -96,7 +97,7 @@ class Item extends BreadcrumbsItem { } } -export class EditorBreadcrumbs implements IEditorBreadcrumbs { +export class BreadcrumbsControl { static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false); @@ -104,11 +105,9 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { private readonly _ckBreadcrumbsVisible: IContextKey; private readonly _ckBreadcrumbsActive: IContextKey; - private readonly _cfEnabled: Config; - - private readonly _disposables = new Array(); private readonly _domNode: HTMLDivElement; private readonly _widget: BreadcrumbsWidget; + private _disposables = new Array(); private _breadcrumbsDisposables = new Array(); private _breadcrumbsPickerShowing = false; @@ -124,9 +123,10 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { @IInstantiationService private readonly _instantiationService: IInstantiationService, @IThemeService private readonly _themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, + @IBreadcrumbsService breadcrumbsService: IBreadcrumbsService, ) { this._domNode = document.createElement('div'); - dom.addClasses(this._domNode, 'editor-breadcrumbs', 'show-file-icons'); + dom.addClasses(this._domNode, 'breadcrumbs-control'); dom.append(container, this._domNode); this._widget = new BreadcrumbsWidget(this._domNode); @@ -135,30 +135,23 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables); this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService)); - this._cfEnabled = Config.create(configurationService, 'breadcrumbs.enabled'); - this._disposables.push(this._cfEnabled.onDidChange(value => { - if (!value) { - this.closeEditor(undefined); - this._editorGroup.relayout(); - } else if (this._editorGroup.activeEditor) { - this.openEditor(this._editorGroup.activeEditor); - this._editorGroup.relayout(); - } - })); + this._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); + this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService); - this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); - this._ckBreadcrumbsActive = EditorBreadcrumbs.CK_BreadcrumbsActive.bindTo(this._contextKeyService); + this._disposables.push(breadcrumbsService.register(this._editorGroup.id, this._widget)); } dispose(): void { - dispose(this._disposables); - this._widget.dispose(); + this._disposables = dispose(this._disposables); + this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables); this._ckBreadcrumbsVisible.reset(); - this._cfEnabled.dispose(); + this._ckBreadcrumbsActive.reset(); + this._widget.dispose(); + this._domNode.remove(); } getPreferredHeight(): number { - return this._cfEnabled.value ? 25 : 0; + return 25; } layout(dim: dom.Dimension): void { @@ -171,17 +164,15 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { dom.toggleClass(this._domNode, 'active', value); } - openEditor(input: EditorInput): void { - if (!this._cfEnabled.value) { - // not enabled -> return early - return; - } - + update(input: IEditorInput): void { this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables); - let uri = input.getResource(); - if (!uri || !this._fileService.canHandleResource(uri)) { - return this.closeEditor(undefined); + if (!input || !input.getResource() || !this._fileService.canHandleResource(input.getResource())) { + // cleanup and return when there is no input or when + // we cannot handle this input + this._ckBreadcrumbsVisible.set(false); + dom.toggleClass(this._domNode, 'hidden', true); + return; } dom.toggleClass(this._domNode, 'hidden', false); @@ -191,13 +182,7 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs { let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService); let listener = model.onDidUpdate(_ => this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService)))); this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService))); - - this._breadcrumbsDisposables.push(model, listener); - } - - closeEditor(input: EditorInput): void { - this._ckBreadcrumbsVisible.set(false); - dom.toggleClass(this._domNode, 'hidden', true); + this._breadcrumbsDisposables = [model, listener]; } focus(): void { @@ -464,32 +449,41 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { //#region config -abstract class Config { +export abstract class BreadcrumbsConfig { name: string; value: T; onDidChange: Event; abstract dispose(): void; - static create(service: IConfigurationService, name: string): Config { + private constructor() { + // internal + } - let value: T = service.getValue(name); - let onDidChange = new Emitter(); - - let listener = service.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(name)) { - value = service.getValue(name); - onDidChange.fire(value); - } - }); + static IsEnabled = BreadcrumbsConfig._stub('breadcrumbs.enabled'); + private static _stub(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig } { return { - name, - get value() { return value; }, - onDidChange: onDidChange.event, - dispose(): void { - listener.dispose(); - onDidChange.dispose(); + bindTo(service) { + let value: T = service.getValue(name); + let onDidChange = new Emitter(); + + let listener = service.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(name)) { + value = service.getValue(name); + onDidChange.fire(value); + } + }); + + return { + name, + get value() { return value; }, + onDidChange: onDidChange.event, + dispose(): void { + listener.dispose(); + onDidChange.dispose(); + } + }; } }; } @@ -517,10 +511,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focus', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT, - when: EditorBreadcrumbs.CK_BreadcrumbsVisible, + when: BreadcrumbsControl.CK_BreadcrumbsVisible, handler(accessor) { - let groups = accessor.get(IEditorGroupsService); - groups.activeGroup.breadcrumbs.focus(); + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + //todo@joh focus last? + breadcrumbs.getWidget(groups.activeGroup.id).domFocus(); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -528,10 +524,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.RightArrow, secondary: [KeyMod.Shift | KeyCode.RightArrow], - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), + when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { - let groups = accessor.get(IEditorGroupsService); - groups.activeGroup.breadcrumbs.focusNext(); + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + breadcrumbs.getWidget(groups.activeGroup.id).focusNext(); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -539,10 +536,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.LeftArrow, secondary: [KeyMod.Shift | KeyCode.LeftArrow], - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), + when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { - let groups = accessor.get(IEditorGroupsService); - groups.activeGroup.breadcrumbs.focusPrev(); + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + breadcrumbs.getWidget(groups.activeGroup.id).focusPrev(); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -550,10 +548,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.Enter, secondary: [KeyCode.UpArrow, KeyCode.Space], - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), + when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { - let groups = accessor.get(IEditorGroupsService); - groups.activeGroup.breadcrumbs.select(); + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + const widget = breadcrumbs.getWidget(groups.activeGroup.id); + widget.setSelected(widget.getFocused()); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -561,7 +561,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive), + when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { let groups = accessor.get(IEditorGroupsService); groups.activeGroup.activeControl.focus(); diff --git a/src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts similarity index 100% rename from src/vs/workbench/browser/parts/editor/editorBreadcrumbsModel.ts rename to src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 5e63f57bfad..248fa7871f5 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -117,6 +117,7 @@ export interface IEditorGroupView extends IDisposable, ISerializableView, IEdito isEmpty(): boolean; setActive(isActive: boolean): void; setLabel(label: string): void; + relayout(): void; shutdown(): void; } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index a31d41c5797..372e4e6999d 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -19,7 +19,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme'; -import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService'; import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl'; import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl'; import { IProgressService } from 'vs/platform/progress/common/progress'; @@ -34,7 +34,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceWorker } from 'vs/base/common/async'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; -import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { join } from 'vs/base/common/paths'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -46,7 +46,6 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { EditorBreadcrumbs } from 'vs/workbench/browser/parts/editor/editorBreadcrumbs'; export class EditorGroupView extends Themable implements IEditorGroupView { @@ -105,9 +104,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private titleContainer: HTMLElement; private titleAreaControl: TitleControl; - private breadcrumbsContainer: HTMLElement; - private breadcrumbsControl: EditorBreadcrumbs; - private progressBar: ProgressBar; private editorContainer: HTMLElement; @@ -194,14 +190,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Title control this.createTitleAreaControl(); - // Breadcrumbs container - this.breadcrumbsContainer = document.createElement('div'); - addClass(this.breadcrumbsContainer, 'editor-breadcrumbs'); - this.element.appendChild(this.breadcrumbsContainer); - - // Breadcrumbs control - this.createEditorBreadcrumbs(); - // Editor container this.editorContainer = document.createElement('div'); addClass(this.editorContainer, 'editor-container'); @@ -409,16 +397,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } - private createEditorBreadcrumbs(): void { - - if (this.breadcrumbsControl) { - this.breadcrumbsControl.dispose(); - clearNode(this.breadcrumbsContainer); - } - - this.breadcrumbsControl = this.scopedInstantiationService.createInstance(EditorBreadcrumbs, this.breadcrumbsContainer, this); - } - private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): TPromise { if (this._group.count === 0) { return TPromise.as(void 0); // nothing to show @@ -615,10 +593,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this._label; } - get breadcrumbs(): IEditorBreadcrumbs { - return this.breadcrumbsControl; - } - get disposed(): boolean { return this._disposed; } @@ -644,8 +618,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update title control this.titleAreaControl.setActive(isActive); - this.breadcrumbsControl.setActive(isActive); - // Update styles this.updateStyles(); @@ -817,8 +789,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Show in title control after editor control because some actions depend on it this.titleAreaControl.openEditor(editor); - this.breadcrumbsControl.openEditor(editor); - return openEditorPromise; } @@ -988,7 +958,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to title control & breadcrumbs this.titleAreaControl.closeEditor(editor); - this.breadcrumbsControl.closeEditor(editor); } private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void { @@ -1375,9 +1344,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.dimension = new Dimension(width, height); // Forward to controls - this.titleAreaControl.layout(new Dimension(this.dimension.width, EDITOR_TITLE_HEIGHT)); - this.breadcrumbsControl.layout(new Dimension(this.dimension.width, this.breadcrumbsControl.getPreferredHeight())); - this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + this.breadcrumbsControl.getPreferredHeight()))); + this.titleAreaControl.layout(new Dimension(this.dimension.width, this.titleAreaControl.getPreferredHeight())); + this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - this.titleAreaControl.getPreferredHeight())); } relayout(): void { @@ -1404,8 +1372,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.titleAreaControl.dispose(); - this.breadcrumbsControl.dispose(); - super.dispose(); } } diff --git a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css similarity index 71% rename from src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css rename to src/vs/workbench/browser/parts/editor/media/breadcrumbs.css index e40ca9ba696..5e274604860 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorbreadcrumbs.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css @@ -3,12 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .editor-breadcrumbs { +.monaco-workbench>.part.editor>.content .breadcrumbs-control.hidden { + display: none; +} + +.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .breadcrumbs-control { opacity: .8; } -.monaco-workbench>.part.editor>.content .editor-group-container .editor-breadcrumbs .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ +.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/ padding-left: 8px; } diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index e7abc5b7357..623775e022e 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -44,8 +44,8 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title { position: relative; - height: 35px; display: flex; + flex-wrap: wrap; box-sizing: border-box; overflow: hidden; } @@ -112,4 +112,4 @@ .monaco-workbench > .part.editor > .content .grid-view-container { width: 100%; height: 100%; -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 2dc523ad8aa..f19919dd009 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -252,4 +252,11 @@ cursor: default; flex: initial; padding-left: 4px; -} \ No newline at end of file + height: 35px; +} + +/* Breadcrumbs */ + +.monaco-workbench > .part.editor > .content .editor-group-container > .title .breadcrumbs-control { + flex: 1 100%; +} diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 8934bb9925f..350c8486751 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -39,6 +39,8 @@ import { addClass, addDisposableListener, hasClass, EventType, EventHelper, remo import { localize } from 'vs/nls'; import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; +import { BreadcrumbsControl, BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; interface IEditorInputLabel { name: string; @@ -55,6 +57,7 @@ export class TabsTitleControl extends TitleControl { private editorToolbarContainer: HTMLElement; private scrollbar: ScrollableElement; private closeOneEditorAction: CloseOneEditorAction; + private breadcrumbs: BreadcrumbsControl; private tabLabelWidgets: ResourceLabel[] = []; private tabLabels: IEditorInputLabel[] = []; @@ -78,9 +81,10 @@ export class TabsTitleControl extends TitleControl { @IMenuService menuService: IMenuService, @IQuickOpenService quickOpenService: IQuickOpenService, @IThemeService themeService: IThemeService, - @IExtensionService extensionService: IExtensionService + @IExtensionService extensionService: IExtensionService, + @IConfigurationService configurationService: IConfigurationService ) { - super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService); + super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService); } protected create(parent: HTMLElement): void { @@ -108,6 +112,9 @@ export class TabsTitleControl extends TitleControl { // Close Action this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL)); + + // Breadcrumbs + this.createBreadcrumbsControl(); } private createScrollbar(): void { @@ -128,6 +135,24 @@ export class TabsTitleControl extends TitleControl { this.titleContainer.appendChild(this.scrollbar.getDomNode()); } + private createBreadcrumbsControl(): void { + const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService)); + config.onDidChange(value => { + if (!value && this.breadcrumbs) { + this.breadcrumbs.dispose(); + this.breadcrumbs = undefined; + this.group.relayout(); + } else if (value && !this.breadcrumbs) { + this.breadcrumbs = this.instantiationService.createInstance(BreadcrumbsControl, this.titleContainer, this.group); + this.breadcrumbs.update(this.group.activeEditor); + this.group.relayout(); + } + }); + if (config.value) { + this.breadcrumbs = this.instantiationService.createInstance(BreadcrumbsControl, this.titleContainer, this.group); + } + } + private registerContainerListeners(): void { // Group dragging @@ -240,6 +265,11 @@ export class TabsTitleControl extends TitleControl { // Redraw all tabs this.redraw(); + + // Update Breadcrumbs + if (this.breadcrumbs) { + this.breadcrumbs.update(editor); + } } closeEditor(editor: IEditorInput): void { @@ -287,6 +317,11 @@ export class TabsTitleControl extends TitleControl { this.clearEditorActionsToolbar(); } + + // Update Breadcrumbs + if (this.breadcrumbs) { + this.breadcrumbs.update(undefined); + } } moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void { @@ -885,6 +920,10 @@ export class TabsTitleControl extends TitleControl { } } + getPreferredHeight(): number { + return super.getPreferredHeight() + (this.breadcrumbs ? this.breadcrumbs.getPreferredHeight() : 0); + } + layout(dimension: Dimension): void { const activeTab = this.getTab(this.group.activeEditor); if (!activeTab || !dimension) { @@ -910,6 +949,13 @@ export class TabsTitleControl extends TitleControl { return; } + let breadcrumbsHeight = 0; + if (this.breadcrumbs) { + breadcrumbsHeight = this.breadcrumbs.getPreferredHeight(); + this.breadcrumbs.layout({ width: dimension.width, height: dimension.height - super.getPreferredHeight() }); + this.scrollbar.getDomNode().style.height = `${dimension.height - breadcrumbsHeight}px`; + } + const visibleContainerWidth = this.tabsContainer.offsetWidth; const totalContainerWidth = this.tabsContainer.scrollWidth; @@ -1039,6 +1085,7 @@ export class TabsTitleControl extends TitleControl { dispose(): void { super.dispose(); + this.breadcrumbs = dispose(this.breadcrumbs); this.layoutScheduled = dispose(this.layoutScheduled); } } @@ -1198,4 +1245,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { `); } } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 8ddd3329287..1316298e7f6 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -33,10 +33,11 @@ import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Dimension, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { LocalSelectionTransfer, DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; import { applyDragImage } from 'vs/base/browser/dnd'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IToolbarActions { primary: IAction[]; @@ -72,7 +73,8 @@ export abstract class TitleControl extends Themable { @IMenuService private menuService: IMenuService, @IQuickOpenService protected quickOpenService: IQuickOpenService, @IThemeService themeService: IThemeService, - @IExtensionService private extensionService: IExtensionService + @IExtensionService private extensionService: IExtensionService, + @IConfigurationService protected configurationService: IConfigurationService ) { super(themeService); @@ -329,6 +331,10 @@ export abstract class TitleControl extends Themable { // Optionally implemented in subclasses } + getPreferredHeight(): number { + return EDITOR_TITLE_HEIGHT; + } + dispose(): void { this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables); diff --git a/src/vs/workbench/services/group/common/editorGroupsService.ts b/src/vs/workbench/services/group/common/editorGroupsService.ts index e744ba63b0f..6bc438e07a4 100644 --- a/src/vs/workbench/services/group/common/editorGroupsService.ts +++ b/src/vs/workbench/services/group/common/editorGroupsService.ts @@ -312,13 +312,6 @@ export interface IGroupChangeEvent { editorIndex?: number; } -export interface IEditorBreadcrumbs { - focus(): void; - focusNext(): void; - focusPrev(): void; - select(): void; -} - export interface IEditorGroup { /** @@ -339,11 +332,6 @@ export interface IEditorGroup { */ readonly label: string; - /** - * - */ - readonly breadcrumbs: IEditorBreadcrumbs; - /** * The active control is the currently visible control of the group. */ diff --git a/src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts similarity index 97% rename from src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts rename to src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts index c0942cd5dc4..6e4f476f5e6 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorBreadcrumbModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel'; +import { EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 35a157ea0c1..d9a1cb607c7 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -67,7 +67,7 @@ import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensi import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService'; import { IEditorService, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -583,7 +583,6 @@ export class TestEditorGroup implements IEditorGroupView { constructor(public id: number) { } group: EditorGroup = void 0; - breadcrumbs: IEditorBreadcrumbs; activeControl: IEditor; activeEditor: IEditorInput; previewEditor: IEditorInput; @@ -674,6 +673,7 @@ export class TestEditorGroup implements IEditorGroupView { dispose(): void { } toJSON(): object { return Object.create(null); } layout(width: number, height: number): void { } + relayout() { } } export class TestEditorService implements EditorServiceImpl { From 682650486dc297f9d560ab1f96e0513586a1951e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 18:10:07 +0200 Subject: [PATCH 103/151] fix breadcrumbs styles --- .../ui/breadcrumbs/breadcrumbsWidget.css | 3 --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 12 +++++----- src/vs/platform/theme/common/colorRegistry.ts | 3 +++ src/vs/platform/theme/common/styler.ts | 24 ++++--------------- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index a7f820dee40..a3c36f783d0 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -35,6 +35,3 @@ background-image: url(./collpased-dark.svg); } -.monaco-breadcrumbs .monaco-breadcrumb-item.focused { - font-weight: bold; -} diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index aa7d97bb081..f3189df65e7 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -44,8 +44,8 @@ export class SimpleBreadcrumbsItem extends BreadcrumbsItem { export interface IBreadcrumbsWidgetStyles { breadcrumbsBackground?: Color; - breadcrumbsActiveForeground?: Color; - breadcrumbsInactiveForeground?: Color; + breadcrumbsForeground?: Color; + breadcrumbsFocusedForeground?: Color; } export interface IBreadcrumbsItemEvent { @@ -123,11 +123,11 @@ export class BreadcrumbsWidget { if (style.breadcrumbsBackground) { content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`; } - if (style.breadcrumbsActiveForeground) { - content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item { color: ${style.breadcrumbsActiveForeground}}\n`; + if (style.breadcrumbsForeground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsForeground}}\n`; } - if (style.breadcrumbsInactiveForeground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsInactiveForeground}}\n`; + if (style.breadcrumbsFocusedForeground) { + content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsFocusedForeground}}\n`; } if (this._styleElement.innerHTML !== content) { this._styleElement.innerHTML = content; diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 241993fb8f8..3aaaee8e0da 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -223,6 +223,9 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); +export const breadcrumbsForegound = registerColor('breadcrumb.focusForeground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC;').transparent(.7), hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsFocusedForegound = registerColor('breadcrumb.breadcrumbsFocusedForegound', { light: '#6C6C6C', dark: '#CCCCCC;', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); + /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index a14be6f3154..72296db3038 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -6,7 +6,7 @@ 'use strict'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForegound, breadcrumbsFocusedForegound } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -265,28 +265,14 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides { breadcrumbsBackground?: ColorIdentifier; - breadcrumbsItemHoverBackground?: ColorIdentifier; - breadcrumbsItemHoverForeground?: ColorIdentifier; - breadcrumbsItemFocusBackground?: ColorIdentifier; - breadcrumbsItemFocusForeground?: ColorIdentifier; - breadcrumbsActiveItemSelectionBackground?: ColorIdentifier; - breadcrumbsActiveItemSelectionForeground?: ColorIdentifier; - breadcrumbsInactiveItemSelectionBackground?: ColorIdentifier; - breadcrumbsInactiveItemSelectionForeground?: ColorIdentifier; + breadcrumbsForeground?: ColorIdentifier; + breadcrumbsFocusedForeground?: ColorIdentifier; } export const defaultBreadcrumbsStyles = { breadcrumbsBackground: editorBackground, - breadcrumbsItemHoverBackground: listHoverBackground, - breadcrumbsItemHoverForeground: listHoverForeground, - breadcrumbsItemFocusBackground: listFocusBackground, - breadcrumbsItemFocusForeground: listFocusForeground, - breadcrumbsItemSelectionBackground: listActiveSelectionBackground, - breadcrumbsItemSelectionForeground: listActiveSelectionForeground, - breadcrumbsActiveItemSelectionBackground: listActiveSelectionBackground, - breadcrumbsActiveItemSelectionForeground: listActiveSelectionForeground, - breadcrumbsInactiveItemSelectionBackground: editorBackground, - breadcrumbsInactiveItemSelectionForeground: listInactiveSelectionForeground, + breadcrumbsForeground: breadcrumbsForegound, + breadcrumbsFocusedForeground: breadcrumbsFocusedForegound }; export function attachBreadcrumbsStyler(widget: IThemable, themeService: IThemeService, style?: IBreadcrumbsWidgetStyleOverrides): IDisposable { From ee58b82937168ce76d376579b35dca1c35cbc773 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 18:12:39 +0200 Subject: [PATCH 104/151] focus editor, unfocus breadcrumbs when dismissing the picker --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 48b68cf6a79..5c0c858705a 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -563,7 +563,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { - let groups = accessor.get(IEditorGroupsService); + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined); groups.activeGroup.activeControl.focus(); } }); From 15701d1be39654746b94c41127ecb5f69eefc45e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 18:15:04 +0200 Subject: [PATCH 105/151] only hide breadcrumbs when last editor goes --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 4 ++-- src/vs/workbench/browser/parts/editor/tabsTitleControl.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 5c0c858705a..cb36cba30a1 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -37,7 +37,6 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; -import { IEditorInput } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -164,7 +163,8 @@ export class BreadcrumbsControl { dom.toggleClass(this._domNode, 'active', value); } - update(input: IEditorInput): void { + update(): void { + const input = this._editorGroup.activeEditor; this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables); if (!input || !input.getResource() || !this._fileService.canHandleResource(input.getResource())) { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 350c8486751..10ff0952b04 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -144,7 +144,7 @@ export class TabsTitleControl extends TitleControl { this.group.relayout(); } else if (value && !this.breadcrumbs) { this.breadcrumbs = this.instantiationService.createInstance(BreadcrumbsControl, this.titleContainer, this.group); - this.breadcrumbs.update(this.group.activeEditor); + this.breadcrumbs.update(); this.group.relayout(); } }); @@ -268,7 +268,7 @@ export class TabsTitleControl extends TitleControl { // Update Breadcrumbs if (this.breadcrumbs) { - this.breadcrumbs.update(editor); + this.breadcrumbs.update(); } } @@ -320,7 +320,7 @@ export class TabsTitleControl extends TitleControl { // Update Breadcrumbs if (this.breadcrumbs) { - this.breadcrumbs.update(undefined); + this.breadcrumbs.update(); } } From b95486df073d4beb1a456749642ab57d107c6ca8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 18:31:18 +0200 Subject: [PATCH 106/151] more color fixes --- .../base/browser/ui/breadcrumbs/breadcrumbsWidget.css | 1 + src/vs/platform/theme/common/colorRegistry.ts | 4 ++-- .../browser/parts/editor/breadcrumbsControl.ts | 10 ++++++---- .../browser/parts/editor/media/breadcrumbs.css | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index a3c36f783d0..adbe04a698d 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -23,6 +23,7 @@ .monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after { background-image: url(./collapsed.svg); + opacity: .7; width: 16px; height: 16px; display: inline-block; diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 3aaaee8e0da..b57f7ea9355 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -223,8 +223,8 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); -export const breadcrumbsForegound = registerColor('breadcrumb.focusForeground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC;').transparent(.7), hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); -export const breadcrumbsFocusedForegound = registerColor('breadcrumb.breadcrumbsFocusedForegound', { light: '#6C6C6C', dark: '#CCCCCC;', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsForegound = registerColor('breadcrumb.focusForeground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC').transparent(.7), hc: Color.white.transparent(.7) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsFocusedForegound = registerColor('breadcrumb.breadcrumbsFocusedForegound', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); /** * Editor background color. diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index cb36cba30a1..4287feef506 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -42,6 +42,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; +import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; class Item extends BreadcrumbsItem { @@ -221,18 +222,19 @@ export class BreadcrumbsControl { }, render: (container: HTMLElement) => { dom.addClasses(container, 'monaco-breadcrumbs-picker', 'monaco-workbench', 'show-file-icons'); - let color = this._themeService.getTheme().getColor(SIDE_BAR_BACKGROUND); - container.style.borderColor = color.darken(.2).toString(); - container.style.boxShadow = `${color.toString()} 6px 6px 6px -6px;`; + const theme = this._themeService.getTheme(); + const color = theme.getColor(editorBackground).darken(theme.type === 'dark' ? .2 : .1); + container.style.borderColor = color.toString(); + container.style.boxShadow = `2px 2px 3px ${color.toString()}`; let { element } = event.item as Item; let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; let res = this._instantiationService.createInstance(ctor, container, element); res.layout({ width: 250, height: 300 }); let listener = res.onDidPickElement(data => { - this._contextViewService.hideContextView(); if (!data) { return; } + this._contextViewService.hideContextView(); if (URI.isUri(data)) { // open new editor this._editorService.openEditor({ resource: data }); diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css index 5e274604860..fe1ca4cedba 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css @@ -17,6 +17,6 @@ } .monaco-breadcrumbs-picker { - border: 1px #dddddd solid; - box-shadow: #dddddd 6px 6px 6px -6px; + border-style: solid; + border-width: 1px; } From 8dae1e2fa2d0ab1f32bd8bfc733b3d994aaad83b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 18:34:31 +0200 Subject: [PATCH 107/151] list: IVirtualDelegate --- src/vs/base/browser/ui/list/list.ts | 2 +- src/vs/base/browser/ui/list/listPaging.ts | 6 +++--- src/vs/base/browser/ui/list/listView.ts | 8 ++++---- src/vs/base/browser/ui/list/listWidget.ts | 6 +++--- .../base/browser/ui/selectBox/selectBoxCustom.ts | 4 ++-- src/vs/base/browser/ui/tree/tree.ts | 16 +++++++++------- src/vs/base/browser/ui/tree/treeModel.ts | 4 +++- .../base/test/browser/ui/list/listView.test.ts | 4 ++-- src/vs/editor/contrib/suggest/suggestWidget.ts | 4 ++-- src/vs/platform/list/browser/listService.ts | 6 +++--- .../parts/notifications/notificationsViewer.ts | 4 ++-- .../browser/parts/quickinput/quickInputList.ts | 4 ++-- .../parts/debug/browser/breakpointsView.ts | 4 ++-- .../electron-browser/extensionsList.ts | 4 ++-- .../electron-browser/runtimeExtensionsEditor.ts | 4 ++-- .../electron-browser/views/openEditorsView.ts | 4 ++-- .../preferences/browser/keybindingsEditor.ts | 4 ++-- .../parts/scm/electron-browser/scmViewlet.ts | 6 +++--- 18 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 150758f8ae7..37ff5439232 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -5,7 +5,7 @@ import { GestureEvent } from 'vs/base/browser/touch'; -export interface IDelegate { +export interface IVirtualDelegate { getHeight(element: T): number; getTemplateId(element: T): string; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index e5735d6c299..10094f4e5a6 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -6,7 +6,7 @@ import 'vs/css!./list'; import { IDisposable } from 'vs/base/common/lifecycle'; import { range } from 'vs/base/common/arrays'; -import { IDelegate, IRenderer, IListEvent, IListOpenEvent } from './list'; +import { IVirtualDelegate, IRenderer, IListEvent, IListOpenEvent } from './list'; import { List, IListStyles, IListOptions } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; import { Event, mapEvent } from 'vs/base/common/event'; @@ -69,12 +69,12 @@ export class PagedList implements IDisposable { constructor( container: HTMLElement, - delegate: IDelegate, + virtualDelegate: IVirtualDelegate, renderers: IPagedRenderer[], options: IListOptions = {} ) { const pagedRenderers = renderers.map(r => new PagedRenderer>(r, () => this.model)); - this.list = new List(container, delegate, pagedRenderers, options); + this.list = new List(container, virtualDelegate, pagedRenderers, options); } getHTMLElement(): HTMLElement { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 2ff18c97783..389493026ec 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -12,7 +12,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility } from 'vs/base/common/scrollable'; import { RangeMap, IRange, relativeComplement, intersect, shift } from './rangeMap'; -import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list'; +import { IVirtualDelegate, IRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; @@ -72,7 +72,7 @@ export class ListView implements ISpliceable, IDisposable { constructor( container: HTMLElement, - private delegate: IDelegate, + private virtualDelegate: IVirtualDelegate, renderers: IRenderer[], options: IListViewOptions = DefaultOptions ) { @@ -156,8 +156,8 @@ export class ListView implements ISpliceable, IDisposable { const inserted = elements.map>(element => ({ id: String(this.itemId++), element, - size: this.delegate.getHeight(element), - templateId: this.delegate.getTemplateId(element), + size: this.virtualDelegate.getHeight(element), + templateId: this.virtualDelegate.getTemplateId(element), row: null })); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 58c81edc992..f8ffd1a0feb 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -15,7 +15,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListOpenEvent } from './list'; +import { IVirtualDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListOpenEvent } from './list'; import { ListView, IListViewOptions } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -883,7 +883,7 @@ export class List implements ISpliceable, IDisposable { constructor( container: HTMLElement, - delegate: IDelegate, + virtualDelegate: IVirtualDelegate, renderers: IRenderer[], options: IListOptions = DefaultOptions ) { @@ -894,7 +894,7 @@ export class List implements ISpliceable, IDisposable { renderers = renderers.map(r => new PipelineRenderer(r.templateId, [this.focus.renderer, this.selection.renderer, r])); - this.view = new ListView(container, delegate, renderers, options); + this.view = new ListView(container, virtualDelegate, renderers, options); this.view.domNode.setAttribute('role', 'tree'); DOM.addClass(this.view.domNode, this.idPrefix); this.view.domNode.tabIndex = 0; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 8185d43aa04..249abc20629 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 dom from 'vs/base/browser/dom'; import * as arrays from 'vs/base/common/arrays'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; import { domEvent } from 'vs/base/browser/event'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; @@ -76,7 +76,7 @@ class SelectListRenderer implements IRenderer { +export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate { private static readonly DEFAULT_DROPDOWN_MINIMUM_BOTTOM_MARGIN = 32; diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 97cfe9659cd..3bc11d3d61b 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -6,9 +6,9 @@ import 'vs/css!./tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IListOptions, List, IIdentityProvider, IMultipleSelectionController } from 'vs/base/browser/ui/list/listWidget'; -import { TreeModel, ITreeNode, ITreeElement, getNodeLocation } from 'vs/base/browser/ui/tree/treeModel'; +import { TreeModel, ITreeNode, ITreeElement, getNodeLocation, ISequence } from 'vs/base/browser/ui/tree/treeModel'; import { IIterator, empty } from 'vs/base/common/iterator'; -import { IDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { append, $ } from 'vs/base/browser/dom'; import { Event, Relay, chain } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -45,9 +45,9 @@ function toTreeListOptions(options?: IListOptions): IListOptions implements IDelegate> { +class TreeDelegate implements IVirtualDelegate> { - constructor(private delegate: IDelegate) { } + constructor(private delegate: IVirtualDelegate) { } getHeight(element: ITreeNode): number { return this.delegate.getHeight(element.element); @@ -131,6 +131,8 @@ function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } +export interface ITreeOptions extends IListOptions { } + export class Tree implements IDisposable { private view: List>; @@ -139,9 +141,9 @@ export class Tree implements IDisposable { constructor( container: HTMLElement, - delegate: IDelegate, + delegate: IVirtualDelegate, renderers: IRenderer[], - options?: IListOptions + options?: ITreeOptions ) { const treeDelegate = new TreeDelegate(delegate); @@ -166,7 +168,7 @@ export class Tree implements IDisposable { onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } - splice(location: number[], deleteCount: number, toInsert: IIterator> = empty()): IIterator> { + splice(location: number[], deleteCount: number, toInsert: ISequence> = empty()): IIterator> { return this.model.splice(location, deleteCount, toInsert); } diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index 153a413f094..c1ee6ac1ab1 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -96,6 +96,8 @@ export function getNodeLocation(node: ITreeNode): number[] { return location.reverse(); } +export type ISequence = IIterator | T[]; + export class TreeModel { private root: IMutableTreeNode = { @@ -113,7 +115,7 @@ export class TreeModel { constructor(private list: ISpliceable>) { } - splice(location: number[], deleteCount: number, toInsert?: IIterator> | ITreeElement[]): IIterator> { + splice(location: number[], deleteCount: number, toInsert?: ISequence>): IIterator> { if (location.length === 0) { throw new Error('Invalid tree location'); } diff --git a/src/vs/base/test/browser/ui/list/listView.test.ts b/src/vs/base/test/browser/ui/list/listView.test.ts index 9abc4a41a73..2dc54b4253f 100644 --- a/src/vs/base/test/browser/ui/list/listView.test.ts +++ b/src/vs/base/test/browser/ui/list/listView.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { ListView } from 'vs/base/browser/ui/list/listView'; -import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; import { range } from 'vs/base/common/arrays'; suite('ListView', function () { @@ -14,7 +14,7 @@ suite('ListView', function () { element.style.height = '200px'; element.style.width = '200px'; - const delegate: IDelegate = { + const delegate: IVirtualDelegate = { getHeight() { return 20; }, getTemplateId() { return 'template'; } }; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 5df91f673f5..3360b6fbda0 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -14,7 +14,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; -import { IDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -352,7 +352,7 @@ export interface ISelectedSuggestion { model: CompletionModel; } -export class SuggestWidget implements IContentWidget, IDelegate, IDisposable { +export class SuggestWidget implements IContentWidget, IVirtualDelegate, IDisposable { private static readonly ID: string = 'editor.widget.suggestWidget'; diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 0a6b5ed6263..d4448b8b1bf 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -10,7 +10,7 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiatio import { IDisposable, toDisposable, combinedDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { PagedList, IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; -import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { attachListStyler, defaultListStyles, computeStyles } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -208,7 +208,7 @@ export class WorkbenchList extends List { constructor( container: HTMLElement, - delegate: IDelegate, + delegate: IVirtualDelegate, renderers: IRenderer[], options: IListOptions, @IContextKeyService contextKeyService: IContextKeyService, @@ -279,7 +279,7 @@ export class WorkbenchPagedList extends PagedList { constructor( container: HTMLElement, - delegate: IDelegate, + delegate: IVirtualDelegate, renderers: IPagedRenderer[], options: IListOptions, @IContextKeyService contextKeyService: IContextKeyService, diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 8179d53863d..7e88bde2337 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; import { clearNode, addClass, removeClass, toggleClass, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import URI from 'vs/base/common/uri'; @@ -26,7 +26,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Severity } from 'vs/platform/notification/common/notification'; -export class NotificationsListDelegate implements IDelegate { +export class NotificationsListDelegate implements IVirtualDelegate { private static readonly ROW_HEIGHT = 42; private static readonly LINE_HEIGHT = 22; diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index bfafcbc7976..6655db6f7bf 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -6,7 +6,7 @@ 'use strict'; import 'vs/css!./quickInput'; -import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; import * as dom from 'vs/base/browser/dom'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; @@ -137,7 +137,7 @@ class ListElementRenderer implements IRenderer { +class ListElementDelegate implements IVirtualDelegate { getHeight(element: ListElement): number { return element.item.detail ? 44 : 22; diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 30980eba976..edf399c0e2a 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -23,7 +23,7 @@ import { basename } from 'vs/base/common/paths'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TPromise } from 'vs/base/common/winjs.base'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list'; import { IEditor } from 'vs/workbench/common/editor'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -221,7 +221,7 @@ export class BreakpointsView extends ViewletPanel { } } -class BreakpointsDelegate implements IDelegate { +class BreakpointsDelegate implements IVirtualDelegate { constructor(private debugService: IDebugService) { // noop diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 928d7afa9c2..1153fd57fb8 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -11,7 +11,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Action } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IDelegate } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { once } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; @@ -38,7 +38,7 @@ export interface ITemplateData { extensionDisposables: IDisposable[]; } -export class Delegate implements IDelegate { +export class Delegate implements IVirtualDelegate { getHeight() { return 62; } getTemplateId() { return 'extension'; } } diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 643a04aa602..0d1d08f98b9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -21,7 +21,7 @@ import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/parts/exte import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService, IExtensionDescription, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; -import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { append, $, addClass, toggleClass, Dimension } from 'vs/base/browser/dom'; import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -216,7 +216,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { const TEMPLATE_ID = 'runtimeExtensionElementTemplate'; - const delegate = new class implements IDelegate{ + const delegate = new class implements IVirtualDelegate{ getHeight(element: IRuntimeExtension): number { return 62; } diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index da4cf28b5f0..52205ce5b1d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -26,7 +26,7 @@ import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { IDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { EditorLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -488,7 +488,7 @@ class OpenEditorActionRunner extends ActionRunner { } } -class OpenEditorsDelegate implements IDelegate { +class OpenEditorsDelegate implements IVirtualDelegate { public static readonly ITEM_HEIGHT = 22; diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 5d9d27c5a07..c071be384ec 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -32,7 +32,7 @@ import { import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -614,7 +614,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor } } -class Delegate implements IDelegate { +class Delegate implements IVirtualDelegate { getHeight(element: IListEntry) { if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 1dc032c14f9..6d60c47a9f4 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -17,7 +17,7 @@ import { PanelViewlet, ViewletPanel, IViewletPanelOptions } from 'vs/workbench/b import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/parts/scm/common/scm'; import { FileLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; @@ -75,7 +75,7 @@ export interface IViewModel { hide(repository: ISCMRepository): void; } -class ProvidersListDelegate implements IDelegate { +class ProvidersListDelegate implements IVirtualDelegate { getHeight(element: ISCMRepository): number { return 22; @@ -575,7 +575,7 @@ class ResourceRenderer implements IRenderer { } } -class ProviderListDelegate implements IDelegate { +class ProviderListDelegate implements IVirtualDelegate { getHeight() { return 22; } From 52ed999139d8c10d3e8ee003d8a6336a9c1d1fa7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 Jul 2018 19:04:57 +0200 Subject: [PATCH 108/151] cleanup splice types --- src/vs/base/browser/ui/list/splice.ts | 4 +- src/vs/base/browser/ui/tree/tree.ts | 6 +- src/vs/base/browser/ui/tree/treeModel.ts | 22 ++-- src/vs/base/common/iterator.ts | 108 ++++++++++-------- src/vs/base/common/linkedList.ts | 4 +- src/vs/base/common/map.ts | 6 +- .../test/browser/ui/tree/treeModel.test.ts | 38 +++--- src/vs/base/test/common/map.test.ts | 4 +- .../common/services/editorSimpleWorker.ts | 6 +- .../decorations/browser/decorationsService.ts | 4 +- 10 files changed, 105 insertions(+), 97 deletions(-) diff --git a/src/vs/base/browser/ui/list/splice.ts b/src/vs/base/browser/ui/list/splice.ts index e65be2e8432..254e25d6a7b 100644 --- a/src/vs/base/browser/ui/list/splice.ts +++ b/src/vs/base/browser/ui/list/splice.ts @@ -5,9 +5,7 @@ 'use strict'; -export interface ISpliceable { - splice(start: number, deleteCount: number, elements: T[]): void; -} +import { ISpliceable } from 'vs/base/common/sequence'; export interface ISpreadSpliceable { splice(start: number, deleteCount: number, ...elements: T[]): void; diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 3bc11d3d61b..18a45d2c74c 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -6,8 +6,8 @@ import 'vs/css!./tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IListOptions, List, IIdentityProvider, IMultipleSelectionController } from 'vs/base/browser/ui/list/listWidget'; -import { TreeModel, ITreeNode, ITreeElement, getNodeLocation, ISequence } from 'vs/base/browser/ui/tree/treeModel'; -import { IIterator, empty } from 'vs/base/common/iterator'; +import { TreeModel, ITreeNode, ITreeElement, getNodeLocation } from 'vs/base/browser/ui/tree/treeModel'; +import { Iterator, ISequence } from 'vs/base/common/iterator'; import { IVirtualDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { append, $ } from 'vs/base/browser/dom'; import { Event, Relay, chain } from 'vs/base/common/event'; @@ -168,7 +168,7 @@ export class Tree implements IDisposable { onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } - splice(location: number[], deleteCount: number, toInsert: ISequence> = empty()): IIterator> { + splice(location: number[], deleteCount: number, toInsert: ISequence> = Iterator.empty()): Iterator> { return this.model.splice(location, deleteCount, toInsert); } diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index c1ee6ac1ab1..1a30ff92292 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -6,12 +6,12 @@ 'use strict'; import { ISpliceable } from 'vs/base/common/sequence'; -import { IIterator, map, collect, iter, empty } from 'vs/base/common/iterator'; +import { Iterator, ISequence } from 'vs/base/common/iterator'; import { Emitter, Event } from 'vs/base/common/event'; export interface ITreeElement { readonly element: T; - readonly children?: IIterator> | ITreeElement[]; + readonly children?: Iterator> | ITreeElement[]; readonly collapsible?: boolean; readonly collapsed?: boolean; } @@ -51,11 +51,11 @@ function getVisibleNodes(nodes: IMutableTreeNode[], result: ITreeNode[] return result; } -function getTreeElementIterator(elements: IIterator> | ITreeElement[] | undefined): IIterator> { +function getTreeElementIterator(elements: Iterator> | ITreeElement[] | undefined): Iterator> { if (!elements) { - return empty(); + return Iterator.empty(); } else if (Array.isArray(elements)) { - return iter(elements); + return Iterator.iterate(elements); } else { return elements; } @@ -71,7 +71,7 @@ function treeElementToNode(treeElement: ITreeElement, parent: IMutableTree } const children = getTreeElementIterator(treeElement.children); - node.children = collect(map(children, el => treeElementToNode(el, node, visible && !treeElement.collapsed, treeListElements))); + node.children = Iterator.collect(Iterator.map(children, el => treeElementToNode(el, node, visible && !treeElement.collapsed, treeListElements))); node.collapsible = node.collapsible || node.children.length > 0; node.visibleCount = 1 + getVisibleCount(node.children); @@ -80,7 +80,7 @@ function treeElementToNode(treeElement: ITreeElement, parent: IMutableTree function treeNodeToElement(node: IMutableTreeNode): ITreeElement { const { element, collapsed } = node; - const children = map(iter(node.children), treeNodeToElement); + const children = Iterator.map(Iterator.iterate(node.children), treeNodeToElement); return { element, children, collapsed }; } @@ -96,8 +96,6 @@ export function getNodeLocation(node: ITreeNode): number[] { return location.reverse(); } -export type ISequence = IIterator | T[]; - export class TreeModel { private root: IMutableTreeNode = { @@ -115,7 +113,7 @@ export class TreeModel { constructor(private list: ISpliceable>) { } - splice(location: number[], deleteCount: number, toInsert?: ISequence>): IIterator> { + splice(location: number[], deleteCount: number, toInsert?: ISequence>): Iterator> { if (location.length === 0) { throw new Error('Invalid tree location'); } @@ -123,7 +121,7 @@ export class TreeModel { const { parentNode, listIndex, visible } = this.findParentNode(location); const treeListElementsToInsert: ITreeNode[] = []; const elementsToInsert = getTreeElementIterator(toInsert); - const nodesToInsert = collect(map(elementsToInsert, el => treeElementToNode(el, parentNode, visible, treeListElementsToInsert))); + const nodesToInsert = Iterator.collect(Iterator.map(elementsToInsert, el => treeElementToNode(el, parentNode, visible, treeListElementsToInsert))); const lastIndex = location[location.length - 1]; const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); const visibleDeleteCount = getVisibleCount(deletedNodes); @@ -134,7 +132,7 @@ export class TreeModel { this.list.splice(listIndex, visibleDeleteCount, treeListElementsToInsert); } - return map(iter(deletedNodes), treeNodeToElement); + return Iterator.map(Iterator.iterate(deletedNodes), treeNodeToElement); } getListIndex(location: number[]): number { diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 778819cadb8..9e6f3c57461 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -5,74 +5,86 @@ 'use strict'; -export interface IIteratorResult { +export interface IteratorResult { readonly done: boolean; readonly value: T | undefined; } -export interface IIterator { - next(): IIteratorResult; +export interface Iterator { + next(): IteratorResult; } -const _empty: IIterator = { - next() { - return { done: true, value: undefined }; +export module Iterator { + const _empty: Iterator = { + next() { + return { done: true, value: undefined }; + } + }; + + export function empty(): Iterator { + return _empty; } -}; -export function empty(): IIterator { - return _empty; -} + export function iterate(array: T[], index = 0, length = array.length): Iterator { + return { + next(): IteratorResult { + if (index >= length) { + return { done: true, value: undefined }; + } -export function iter(array: T[], index = 0, length = array.length): IIterator { - return { - next(): IIteratorResult { - if (index >= length) { - return { done: true, value: undefined }; + return { done: false, value: array[index++] }; } + }; + } - return { done: false, value: array[index++] }; - } - }; -} - -export function map(iterator: IIterator, fn: (t: T) => R): IIterator { - return { - next() { - const { done, value } = iterator.next(); - return { done, value: done ? undefined : fn(value) }; - } - }; -} - -export function filter(iterator: IIterator, fn: (t: T) => boolean): IIterator { - return { - next() { - while (true) { + export function map(iterator: Iterator, fn: (t: T) => R): Iterator { + return { + next() { const { done, value } = iterator.next(); + return { done, value: done ? undefined : fn(value) }; + } + }; + } - if (done) { - return { done, value: undefined }; - } + export function filter(iterator: Iterator, fn: (t: T) => boolean): Iterator { + return { + next() { + while (true) { + const { done, value } = iterator.next(); - if (fn(value)) { - return { done, value }; + if (done) { + return { done, value: undefined }; + } + + if (fn(value)) { + return { done, value }; + } } } - } - }; -} + }; + } -export function forEach(iterator: IIterator, fn: (t: T) => void): void { - for (let next = iterator.next(); !next.done; next = iterator.next()) { - fn(next.value); + export function forEach(iterator: Iterator, fn: (t: T) => void): void { + for (let next = iterator.next(); !next.done; next = iterator.next()) { + fn(next.value); + } + } + + export function collect(iterator: Iterator): T[] { + const result: T[] = []; + forEach(iterator, value => result.push(value)); + return result; } } -export function collect(iterator: IIterator): T[] { - const result: T[] = []; - forEach(iterator, value => result.push(value)); - return result; +export type ISequence = Iterator | T[]; + +export function getSequenceIterator(arg: Iterator | T[]): Iterator { + if (Array.isArray(arg)) { + return Iterator.iterate(arg); + } else { + return arg; + } } export interface INextIterator { diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index d40fd2a2861..9f44422e99c 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IIterator } from 'vs/base/common/iterator'; +import { Iterator } from 'vs/base/common/iterator'; class Node { element: E; @@ -94,7 +94,7 @@ export class LinkedList { }; } - iterator(): IIterator { + iterator(): Iterator { let element = { done: undefined, value: undefined, diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index e234c796330..c39465b4991 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -7,7 +7,7 @@ import URI from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; -import { IIterator } from './iterator'; +import { Iterator } from './iterator'; export function values(set: Set): V[]; export function values(map: Map): V[]; @@ -306,7 +306,7 @@ export class TernarySearchTree { return node && node.value || candidate; } - findSuperstr(key: string): IIterator { + findSuperstr(key: string): Iterator { let iter = this._iter.reset(key); let node = this._root; while (node) { @@ -333,7 +333,7 @@ export class TernarySearchTree { return undefined; } - private _nodeIterator(node: TernarySearchTreeNode): IIterator { + private _nodeIterator(node: TernarySearchTreeNode): Iterator { let res = { done: false, value: undefined diff --git a/src/vs/base/test/browser/ui/tree/treeModel.test.ts b/src/vs/base/test/browser/ui/tree/treeModel.test.ts index a4901161329..f348b38a9d9 100644 --- a/src/vs/base/test/browser/ui/tree/treeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/treeModel.test.ts @@ -5,8 +5,8 @@ import * as assert from 'assert'; import { TreeModel, ITreeNode } from 'vs/base/browser/ui/tree/treeModel'; -import { ISpliceable } from 'vs/base/browser/ui/list/splice'; -import { iter } from 'vs/base/common/iterator'; +import { ISpliceable } from 'vs/base/common/sequence'; +import { Iterator } from 'vs/base/common/iterator'; function toSpliceable(arr: T[]): ISpliceable { return { @@ -33,7 +33,7 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { element: 0 }, { element: 1 }, { element: 2 } @@ -55,9 +55,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, children: iter([ + element: 0, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -92,9 +92,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, collapsed: true, children: iter([ + element: 0, collapsed: true, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -120,7 +120,7 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { element: 0 }, { element: 1 }, { element: 2 } @@ -145,9 +145,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, children: iter([ + element: 0, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -179,9 +179,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, children: iter([ + element: 0, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -207,9 +207,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, collapsed: true, children: iter([ + element: 0, collapsed: true, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -232,9 +232,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, children: iter([ + element: 0, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -263,9 +263,9 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { - element: 0, collapsed: true, children: iter([ + element: 0, collapsed: true, children: Iterator.iterate([ { element: 10 }, { element: 11 }, { element: 12 }, @@ -303,7 +303,7 @@ suite('TreeModel2', function () { const list = [] as ITreeNode[]; const model = new TreeModel(toSpliceable(list)); - model.splice([0], 0, iter([ + model.splice([0], 0, Iterator.iterate([ { element: 1, children: [ { diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 23bbe9352ce..07fc6c90f8a 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -8,7 +8,7 @@ import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map'; import * as assert from 'assert'; import URI from 'vs/base/common/uri'; -import { IIteratorResult } from 'vs/base/common/iterator'; +import { IteratorResult } from 'vs/base/common/iterator'; suite('Map', () => { @@ -419,7 +419,7 @@ suite('Map', () => { map.set('/user/foo/flip/flop', 3); map.set('/usr/foo', 4); - let item: IIteratorResult; + let item: IteratorResult; let iter = map.findSuperstr('/user'); item = iter.next(); diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index f78056b1667..5e5f192fb1a 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -22,7 +22,7 @@ import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; import { IWordAtPosition, EndOfLineSequence } from 'vs/editor/common/model'; import { globals } from 'vs/base/common/platform'; -import { IIterator } from 'vs/base/common/iterator'; +import { Iterator } from 'vs/base/common/iterator'; export interface IMirrorModel { readonly uri: URI; @@ -59,7 +59,7 @@ export interface ICommonModel { getLinesContent(): string[]; getLineCount(): number; getLineContent(lineNumber: number): string; - createWordIterator(wordDefinition: RegExp): IIterator; + createWordIterator(wordDefinition: RegExp): Iterator; getWordUntilPosition(position: IPosition, wordDefinition: RegExp): IWordAtPosition; getValueInRange(range: IRange): string; getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range; @@ -147,7 +147,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel { }; } - public createWordIterator(wordDefinition: RegExp): IIterator { + public createWordIterator(wordDefinition: RegExp): Iterator { let obj = { done: false, value: '' diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 146bafc26a3..722deba856e 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -14,7 +14,7 @@ import { LinkedList } from 'vs/base/common/linkedList'; import { createStyleSheet, createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { IdGenerator } from 'vs/base/common/idGenerator'; -import { IIterator } from 'vs/base/common/iterator'; +import { Iterator } from 'vs/base/common/iterator'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -174,7 +174,7 @@ class DecorationStyles { }); } - cleanUp(iter: IIterator): void { + cleanUp(iter: Iterator): void { // remove every rule for which no more // decoration (data) is kept. this isn't cheap let usedDecorations = new Set(); From 0c27d9b38e1ae2572e32475abde13d29f2eb71bf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Jul 2018 19:16:39 +0200 Subject: [PATCH 109/151] remove debug setting --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 4287feef506..e8f7c123321 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -231,10 +231,10 @@ export class BreadcrumbsControl { let res = this._instantiationService.createInstance(ctor, container, element); res.layout({ width: 250, height: 300 }); let listener = res.onDidPickElement(data => { + this._contextViewService.hideContextView(); if (!data) { return; } - this._contextViewService.hideContextView(); if (URI.isUri(data)) { // open new editor this._editorService.openEditor({ resource: data }); From 638b4691af43f810d8438d05e9956dbb745559f9 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 10 Jul 2018 10:36:04 -0700 Subject: [PATCH 110/151] Add buttons to go to extensions config files (#53508) --- .../electron-browser/extensionsActions.ts | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 88964425ea0..ce4e5210290 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -2321,7 +2321,12 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure } return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId, shouldRecommend).then(() => { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.')); + this.notificationService.prompt(Severity.Info, + localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'), + [{ + label: localize('viewChanges', "View Changes"), + run: () => this.openExtensionsFile(configurationFile) + }]); }, err => { this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); }); @@ -2333,7 +2338,12 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure } return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId, shouldRecommend).then(() => { - this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.')); + this.notificationService.prompt(Severity.Info, + localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'), + [{ + label: localize('viewChanges', "View Changes"), + run: () => this.openExtensionsFile(configurationFile) + }]); }, err => { this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); }); @@ -2381,7 +2391,13 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm } return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId, shouldRecommend).then(() => { - this.notificationService.info(localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.')); + this.notificationService.prompt(Severity.Info, + localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'), + [{ + label: localize('viewChanges', "View Changes"), + run: () => this.openWorkspaceConfigurationFile(workspaceConfig) + }]); + }, err => { this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); }); @@ -2392,7 +2408,12 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm } return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId, shouldRecommend).then(() => { - this.notificationService.info(localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.')); + this.notificationService.prompt(Severity.Info, + localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'), + [{ + label: localize('viewChanges', "View Changes"), + run: () => this.openWorkspaceConfigurationFile(workspaceConfig) + }]); }, err => { this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); }); From fa9d8653f5bd76b99fe03dcf9bb3ba7de8cc2c86 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 10 Jul 2018 11:05:22 -0700 Subject: [PATCH 111/151] Allow text selection in comments --- .../workbench/parts/comments/electron-browser/media/review.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index 9854ade1898..b39b972ab61 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -55,6 +55,7 @@ .monaco-editor .review-widget .body .review-comment .review-comment-contents { margin-left: 20px; + user-select: text; } .monaco-editor .review-widget .body pre { From 2ea84289f094d3e8d8241b374dcec993ebf43913 Mon Sep 17 00:00:00 2001 From: Geoffrey Date: Tue, 10 Jul 2018 20:16:55 +0200 Subject: [PATCH 112/151] Permit to update path when export(s) keyword in file (#53964) --- .../src/features/updatePathsOnRename.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 01cf885a5e6..3d5558d4b7a 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -87,7 +87,7 @@ export class UpdateImportsOnFileRenameHandler { // Never attempt to update import paths if the file does not contain something the looks like an export const tree = await this.client.execute('navtree', { file: newFile }); const hasExport = (node: Proto.NavigationTree): boolean => { - return !!node.kindModifiers.match(/\bexport\b/g) || !!(node.childItems && node.childItems.some(hasExport)); + return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); }; if (!tree.body || !tree.body || !hasExport(tree.body)) { return; From e1cbd99173b2cda41b02d3f3a13b8051f2bd627e Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 10 Jul 2018 11:23:02 -0700 Subject: [PATCH 113/151] use alt to focus the menu and and navigation, better focus management (#53924) * use alt to focus the menu and and navigation * updating action runner to return focus properly and set state * return focus in any case of lost focus except intentionally focusing elsewhere * convert SetTimeout to RunOnceScheduler to fix builder issue --- src/vs/base/browser/dom.ts | 2 + src/vs/base/browser/ui/menu/menu.ts | 36 ++- .../parts/menubar/media/menubarpart.css | 3 + .../browser/parts/menubar/menubarPart.ts | 268 ++++++++++++++---- 4 files changed, 233 insertions(+), 76 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 02b3d4115ea..e8ed9bec491 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -840,6 +840,8 @@ export const EventType = { SUBMIT: 'submit', RESET: 'reset', FOCUS: 'focus', + FOCUS_IN: 'focusin', + FOCUS_OUT: 'focusout', BLUR: 'blur', INPUT: 'input', // Local Storage diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index f26829f9edd..3851600c6df 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -15,6 +15,7 @@ import { Event } from 'vs/base/common/event'; import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { $, Builder } from 'vs/base/browser/builder'; +import { RunOnceScheduler } from 'vs/base/common/async'; export interface IMenuOptions { context?: any; @@ -241,6 +242,8 @@ class SubmenuActionItem extends MenuActionItem { private mysubmenu: Menu; private submenuContainer: Builder; private mouseOver: boolean; + private showScheduler: RunOnceScheduler; + private hideScheduler: RunOnceScheduler; constructor( action: IAction, @@ -249,6 +252,20 @@ class SubmenuActionItem extends MenuActionItem { private submenuOptions?: IMenuOptions ) { super(action, action, { label: true, isMenu: true }); + + this.showScheduler = new RunOnceScheduler(() => { + if (this.mouseOver) { + this.cleanupExistingSubmenu(false); + this.createSubmenu(); + } + }, 250); + + this.hideScheduler = new RunOnceScheduler(() => { + if (!this.mouseOver && this.parentData.submenu === this.mysubmenu) { + this.parentData.parent.focus(); + this.cleanupExistingSubmenu(true); + } + }, 750); } public render(container: HTMLElement): void { @@ -278,26 +295,14 @@ class SubmenuActionItem extends MenuActionItem { if (!this.mouseOver) { this.mouseOver = true; - setTimeout(() => { - if (this.mouseOver) { - this.cleanupExistingSubmenu(false); - this.createSubmenu(); - } - }, 250); - + this.showScheduler.schedule(); } }); $(this.builder).on(EventType.MOUSE_LEAVE, (e) => { this.mouseOver = false; - setTimeout(() => { - if (!this.mouseOver && this.parentData.submenu === this.mysubmenu) { - this.parentData.parent.focus(); - this.cleanupExistingSubmenu(true); - } - - }, 750); + this.hideScheduler.schedule(); }); } @@ -360,6 +365,9 @@ class SubmenuActionItem extends MenuActionItem { public dispose() { super.dispose(); + this.hideScheduler.dispose(); + this.showScheduler.dispose(); + if (this.mysubmenu) { this.mysubmenu.dispose(); this.mysubmenu = null; diff --git a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css index 62b8c849063..55191232bcd 100644 --- a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css +++ b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css @@ -32,12 +32,15 @@ zoom: 1; } + .monaco-workbench > .part.menubar > .menubar-menu-button.open, +.monaco-workbench > .part.menubar > .menubar-menu-button:focus, .monaco-workbench > .part.menubar > .menubar-menu-button:hover { background-color: rgba(255, 255, 255, 0.1); } .monaco-workbench > .part.menubar.light > .menubar-menu-button.open, +.monaco-workbench > .part.menubar.light > .menubar-menu-button:focus, .monaco-workbench > .part.menubar.light > .menubar-menu-button:hover { background-color: rgba(0, 0, 0, 0.1); } diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index 8143f27c179..0c2586a7305 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -43,6 +43,13 @@ interface CustomMenu { actions?: IAction[]; } +enum MenubarState { + HIDDEN, + VISIBLE, + FOCUSED, + OPEN +} + export class MenubarPart extends Part { private keys = [ @@ -84,19 +91,22 @@ export class MenubarPart extends Part { private focusedMenu: { index: number; - holder: Builder; - widget: Menu; + holder?: Builder; + widget?: Menu; }; private customMenus: CustomMenu[]; private menuUpdater: RunOnceScheduler; private actionRunner: IActionRunner; + private focusToReturn: Builder; private container: Builder; private recentlyOpened: IRecentlyOpened; private updatePending: boolean; + private focusingWithAlt: boolean; private _modifierKeyStatus: IModifierKeyStatus; - private _isFocused: boolean; + private _focusState: MenubarState; + private _onVisibilityChange: Emitter; private initialSizing: { @@ -143,9 +153,7 @@ export class MenubarPart extends Part { this.actionRunner = this._register(new ActionRunner()); this._register(this.actionRunner.onDidBeforeRun(() => { - if (this.focusedMenu && this.focusedMenu.holder) { - this.focusedMenu.holder.hide(); - } + this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE; })); this._onVisibilityChange = this._register(new Emitter()); @@ -157,7 +165,7 @@ export class MenubarPart extends Part { this.doSetupMenubar(); } - this.isFocused = false; + this._focusState = MenubarState.HIDDEN; this.windowService.getRecentlyOpened().then((recentlyOpened) => { this.recentlyOpened = recentlyOpened; @@ -213,12 +221,12 @@ export class MenubarPart extends Part { return this.configurationService.getValue('window.titleBarStyle'); } - private get isFocused(): boolean { - return this._isFocused; + private get focusState(): MenubarState { + return this._focusState; } - private set isFocused(value: boolean) { - if (this._isFocused && !value) { + private set focusState(value: MenubarState) { + if (this._focusState >= MenubarState.FOCUSED && value < MenubarState.FOCUSED) { // Losing focus, update the menu if needed if (this.updatePending) { @@ -227,13 +235,91 @@ export class MenubarPart extends Part { } } - this._isFocused = value; - - if (!this._isFocused && this.currentMenubarVisibility === 'toggle') { - if (this.container) { - this.hideMenubar(); - } + if (value === this._focusState) { + return; } + + switch (value) { + case MenubarState.HIDDEN: + if (this.isVisible) { + this.hideMenubar(); + } + + if (this.isOpen) { + this.cleanupCustomMenu(); + } + + if (this.isFocused) { + this.focusedMenu = null; + + if (this.focusToReturn) { + this.focusToReturn.domFocus(); + this.focusToReturn = null; + } + } + + + break; + case MenubarState.VISIBLE: + if (!this.isVisible) { + this.showMenubar(); + } + + if (this.isOpen) { + this.cleanupCustomMenu(); + } + + if (this.isFocused) { + if (this.focusedMenu) { + this.customMenus[this.focusedMenu.index].buttonElement.domBlur(); + } + + this.focusedMenu = null; + + if (this.focusToReturn) { + this.focusToReturn.domFocus(); + this.focusToReturn = null; + } + } + + break; + case MenubarState.FOCUSED: + if (!this.isVisible) { + this.showMenubar(); + } + + if (this.isOpen) { + this.cleanupCustomMenu(); + } + + if (this.focusedMenu) { + this.customMenus[this.focusedMenu.index].buttonElement.domFocus(); + } + break; + case MenubarState.OPEN: + if (!this.isVisible) { + this.showMenubar(); + } + + if (this.focusedMenu) { + this.showCustomMenu(this.focusedMenu.index); + } + break; + } + + this._focusState = value; + } + + private get isVisible(): boolean { + return this.focusState >= MenubarState.VISIBLE; + } + + private get isFocused(): boolean { + return this.focusState >= MenubarState.FOCUSED; + } + + private get isOpen(): boolean { + return this.focusState >= MenubarState.OPEN; } private onDidChangeFullscreen(): void { @@ -256,26 +342,42 @@ export class MenubarPart extends Part { this.container.style('visibility', null); } - private onModifierKeyToggled(modiferKeyStatus: IModifierKeyStatus): void { + private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void { + const altKeyPressed = (!this._modifierKeyStatus || !this._modifierKeyStatus.altKey) && modifierKeyStatus.altKey; + const altKeyAlone = altKeyPressed && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; + const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; + if (this.currentMenubarVisibility === 'toggle') { - const altKeyPressed = (!this._modifierKeyStatus || !this._modifierKeyStatus.altKey) && modiferKeyStatus.altKey; - if (altKeyPressed && !modiferKeyStatus.ctrlKey && !modiferKeyStatus.shiftKey) { - this.showMenubar(); - } else if (!this.isFocused) { - this.hideMenubar(); + if (altKeyAlone) { + if (!this.isVisible) { + this.focusState = MenubarState.VISIBLE; + } + } else if (!allModifiersReleased && !this.isFocused) { + this.focusState = MenubarState.HIDDEN; } } - this._modifierKeyStatus = modiferKeyStatus; + if (allModifiersReleased && this.focusingWithAlt) { + if (!this.isFocused) { + this.focusedMenu = { index: 0 }; + this.focusState = MenubarState.FOCUSED; + } else if (!this.isOpen) { + this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE; + } + } + + this._modifierKeyStatus = modifierKeyStatus; if (this.currentEnableMenuBarMnemonics && this.customMenus) { this.customMenus.forEach(customMenu => { let child = customMenu.titleElement.child(); if (child) { - child.style('text-decoration', modiferKeyStatus.altKey ? 'underline' : null); + child.style('text-decoration', modifierKeyStatus.altKey ? 'underline' : null); } }); } + + this.focusingWithAlt = altKeyAlone; } private onRecentlyOpenedChange(): void { @@ -473,7 +575,7 @@ export class MenubarPart extends Part { // Create the top level menu button element if (firstTimeSetup) { - const buttonElement = $(this.container).div({ class: 'menubar-menu-button' }).attr('role', 'menu'); + const buttonElement = $(this.container).div({ class: 'menubar-menu-button' }).attr({ 'role': 'menu', 'tabindex': 0 }); buttonElement.attr('aria-label', this.topLevelTitles[menuTitle].replace(/&&(.)/g, '$1')); const titleElement = $(buttonElement).div({ class: 'menubar-menu-title', 'aria-hidden': true }); @@ -533,30 +635,56 @@ export class MenubarPart extends Part { updateActions(menu, this.customMenus[menuIndex].actions); if (firstTimeSetup) { + this.customMenus[menuIndex].buttonElement.on(EventType.KEY_UP, (e) => { + let event = new StandardKeyboardEvent(e as KeyboardEvent); + let eventHandled = true; + + if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) { + this.focusedMenu = { index: menuIndex }; + this.focusState = MenubarState.OPEN; + } else { + eventHandled = false; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + }); + this.customMenus[menuIndex].buttonElement.on(EventType.CLICK, () => { if (this._modifierKeyStatus && (this._modifierKeyStatus.shiftKey || this._modifierKeyStatus.ctrlKey)) { return; // supress keyboard shortcuts that shouldn't conflict } - this.toggleCustomMenu(menuIndex); - this.isFocused = !this.isFocused; + if (this.isOpen) { + if (this.isCurrentMenu(menuIndex)) { + this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE; + } else { + this.cleanupCustomMenu(); + this.showCustomMenu(menuIndex); + } + } else { + this.focusedMenu = { index: menuIndex }; + this.focusState = MenubarState.OPEN; + } }); this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_ENTER, () => { - if (this.isFocused && !this.isCurrentMenu(menuIndex)) { - this.toggleCustomMenu(menuIndex); + if (this.isOpen && !this.isCurrentMenu(menuIndex)) { + this.customMenus[menuIndex].buttonElement.domFocus(); + this.cleanupCustomMenu(); + this.showCustomMenu(menuIndex); + } else if (this.isFocused && !this.isOpen) { + this.customMenus[menuIndex].buttonElement.domFocus(); } }); this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_LEAVE, () => { - if (!this.isFocused) { - this.cleanupCustomMenu(); + if (!this.isOpen && this.isFocused) { + this.customMenus[menuIndex].buttonElement.domBlur(); } }); - - this.customMenus[menuIndex].buttonElement.on(EventType.BLUR, () => { - this.cleanupCustomMenu(); - }); } } @@ -569,6 +697,8 @@ export class MenubarPart extends Part { this.focusPrevious(); } else if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Tab)) { this.focusNext(); + } else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) { + this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE; } else { eventHandled = false; } @@ -579,9 +709,31 @@ export class MenubarPart extends Part { } }); } + + this.container.on(EventType.FOCUS_IN, (e) => { + let event = e as FocusEvent; + + if (event.relatedTarget) { + if (!this.container.getHTMLElement().contains(event.relatedTarget as HTMLElement)) { + this.focusToReturn = $(event.relatedTarget as HTMLElement); + } + } + }); + + this.container.on(EventType.FOCUS_OUT, (e) => { + let event = e as FocusEvent; + + if (event.relatedTarget) { + if (!this.container.getHTMLElement().contains(event.relatedTarget as HTMLElement)) { + this.focusToReturn = null; + this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE; + } + } + }); } private focusPrevious(): void { + if (!this.focusedMenu) { return; } @@ -592,7 +744,13 @@ export class MenubarPart extends Part { return; } - this.toggleCustomMenu(newFocusedIndex); + if (this.isOpen) { + this.cleanupCustomMenu(); + this.showCustomMenu(newFocusedIndex); + } else if (this.isFocused) { + this.focusedMenu.index = newFocusedIndex; + this.customMenus[newFocusedIndex].buttonElement.domFocus(); + } } private focusNext(): void { @@ -606,7 +764,13 @@ export class MenubarPart extends Part { return; } - this.toggleCustomMenu(newFocusedIndex); + if (this.isOpen) { + this.cleanupCustomMenu(); + this.showCustomMenu(newFocusedIndex); + } else if (this.isFocused) { + this.focusedMenu.index = newFocusedIndex; + this.customMenus[newFocusedIndex].buttonElement.domFocus(); + } } private getMenubarMenus(): IMenubarData { @@ -665,32 +829,14 @@ export class MenubarPart extends Part { if (this.focusedMenu.widget) { this.focusedMenu.widget.dispose(); } + + this.focusedMenu = { index: this.focusedMenu.index }; } - - this.focusedMenu = null; } - public focusCustomMenu(menuTitle: string): void { - this.toggleCustomMenu(0); - } - - private toggleCustomMenu(menuIndex: number): void { + private showCustomMenu(menuIndex: number): void { const customMenu = this.customMenus[menuIndex]; - if (this.focusedMenu) { - let hiding: boolean = this.isCurrentMenu(menuIndex); - - // Need to cleanup currently displayed menu - this.cleanupCustomMenu(); - - // Hiding this menu - if (hiding) { - return; - } - } - - customMenu.buttonElement.domFocus(); - let menuHolder = $(customMenu.buttonElement).div({ class: 'menubar-menu-items-holder' }); $(menuHolder.getHTMLElement().parentElement).addClass('open'); @@ -711,14 +857,12 @@ export class MenubarPart extends Part { let menuWidget = this._register(new Menu(menuHolder.getHTMLElement(), customMenu.actions, menuOptions)); this._register(menuWidget.onDidCancel(() => { - this.cleanupCustomMenu(); - this.isFocused = false; + this.focusState = MenubarState.FOCUSED; })); this._register(menuWidget.onDidBlur(() => { setTimeout(() => { this.cleanupCustomMenu(); - this.isFocused = false; }, 100); })); From 63c7c90ba5393a42a043e18bce6c0c4eed6d9d19 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 11:18:23 -0700 Subject: [PATCH 114/151] Gate workaround for #52967 to TS 2.9 --- .../src/features/updatePathsOnRename.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 3d5558d4b7a..26c0efcd8d0 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -83,14 +83,16 @@ export class UpdateImportsOnFileRenameHandler { this.client.bufferSyncSupport.closeResource(targetResource); this.client.bufferSyncSupport.openTextDocument(document); - // Workaround for https://github.com/Microsoft/vscode/issues/52967 - // Never attempt to update import paths if the file does not contain something the looks like an export - const tree = await this.client.execute('navtree', { file: newFile }); - const hasExport = (node: Proto.NavigationTree): boolean => { - return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); - }; - if (!tree.body || !tree.body || !hasExport(tree.body)) { - return; + if (!this.client.apiVersion.gte(API.v300)) { + // Workaround for https://github.com/Microsoft/vscode/issues/52967 + // Never attempt to update import paths if the file does not contain something the looks like an export + const tree = await this.client.execute('navtree', { file: newFile }); + const hasExport = (node: Proto.NavigationTree): boolean => { + return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); + }; + if (!tree.body || !tree.body || !hasExport(tree.body)) { + return; + } } const edits = await this.getEditsForFileRename(targetFile, document, oldFile, newFile); From 931ec139f1fba863b162f6b04640396845aec2b8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 11:39:45 -0700 Subject: [PATCH 115/151] Don't check nav tree for directory rename Fixes https://github.com/Microsoft/TypeScript/issues/25466 --- .../src/features/updatePathsOnRename.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 26c0efcd8d0..6bc9f265ec5 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -83,15 +83,19 @@ export class UpdateImportsOnFileRenameHandler { this.client.bufferSyncSupport.closeResource(targetResource); this.client.bufferSyncSupport.openTextDocument(document); - if (!this.client.apiVersion.gte(API.v300)) { + if (!this.client.apiVersion.gte(API.v300) && !fs.lstatSync(newResource.fsPath).isDirectory()) { // Workaround for https://github.com/Microsoft/vscode/issues/52967 // Never attempt to update import paths if the file does not contain something the looks like an export - const tree = await this.client.execute('navtree', { file: newFile }); - const hasExport = (node: Proto.NavigationTree): boolean => { - return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); - }; - if (!tree.body || !tree.body || !hasExport(tree.body)) { - return; + try { + const tree = await this.client.execute('navtree', { file: newFile }); + const hasExport = (node: Proto.NavigationTree): boolean => { + return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); + }; + if (!tree.body || !tree.body || !hasExport(tree.body)) { + return; + } + } catch { + // noop } } From 7ad06d9b7ca554dc793eae4fbecbea581cbc5360 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 10 Jul 2018 14:17:35 -0700 Subject: [PATCH 116/151] Minor typo fix --- src/vs/workbench/api/electron-browser/mainThreadTask.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index a3e9c819851..cafb5b305b2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -332,7 +332,7 @@ namespace TaskDTO { let definition = TaskDefinitionDTO.to(task.definition, executeOnly); let id = `${task.source.extensionId}.${definition._key}`; let result: ContributedTask = { - _id: id, // uuidMap.getUUID(identifier), + _id: id, // uuidMap.getUUID(identifier) _source: source, _label: label, type: definition.type, From a92506639fc7e67972174cf1bb282bdb4173d03f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 14:33:09 -0700 Subject: [PATCH 117/151] Don't make extra `findFiles` call on TS 3.0 WIth TS 3.0, we don't need to pass a js or ts file for the project --- .../src/features/updatePathsOnRename.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 6bc9f265ec5..9d7133f6f79 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -209,7 +209,12 @@ export class UpdateImportsOnFileRenameHandler { return undefined; } - if (this.client.apiVersion.gte(API.v292) && fs.lstatSync(resource.fsPath).isDirectory()) { + const isDirectory = fs.lstatSync(resource.fsPath).isDirectory(); + if (isDirectory && this.client.apiVersion.gte(API.v300)) { + return resource; + } + + if (isDirectory && this.client.apiVersion.gte(API.v292)) { const files = await vscode.workspace.findFiles({ base: resource.fsPath, pattern: '**/*.{ts,tsx,js,jsx}', From 473d8887b203a029be8e551c267aea33e337c253 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 14:33:26 -0700 Subject: [PATCH 118/151] Reuse isTypeScriptDocument --- .../src/features/updatePathsOnRename.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 9d7133f6f79..9b2f3c3ce5f 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -10,11 +10,11 @@ import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import * as languageIds from '../utils/languageModeIds'; +import * as fileSchemes from '../utils/fileSchemes'; +import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { escapeRegExp } from '../utils/regexp'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; -import * as fileSchemes from '../utils/fileSchemes'; -import { escapeRegExp } from '../utils/regexp'; const localize = nls.loadMessageBundle(); @@ -304,6 +304,3 @@ export class UpdateImportsOnFileRenameHandler { } } -function isTypeScriptDocument(document: vscode.TextDocument) { - return document.languageId === languageIds.typescript || document.languageId === languageIds.typescriptreact; -} From 5f8b9ab083ba73e6bee26573e91f112e300cb917 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 14:41:59 -0700 Subject: [PATCH 119/151] Remove active editor listener --- .../src/features/tagCompletion.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/extensions/typescript-language-features/src/features/tagCompletion.ts b/extensions/typescript-language-features/src/features/tagCompletion.ts index bcc145d9089..0a01b398e7e 100644 --- a/extensions/typescript-language-features/src/features/tagCompletion.ts +++ b/extensions/typescript-language-features/src/features/tagCompletion.ts @@ -23,13 +23,8 @@ class TagClosing { ) { vscode.workspace.onDidChangeTextDocument( event => this.onDidChangeTextDocument(event.document, event.contentChanges), - null, this.disposables); - - vscode.window.onDidChangeActiveTextEditor( - () => this.updateEnabledState(), - null, this.disposables); - - this.updateEnabledState(); + null, + this.disposables); } public dispose() { @@ -38,10 +33,6 @@ class TagClosing { this.timeout = undefined; } - private updateEnabledState() { - - } - private onDidChangeTextDocument( document: vscode.TextDocument, changes: vscode.TextDocumentContentChangeEvent[] From cbbb21c7651c3a041dac3c012449b10e165112eb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 14:43:26 -0700 Subject: [PATCH 120/151] Don't try closing for cases like
> when user has just typed last > --- .../src/features/tagCompletion.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/typescript-language-features/src/features/tagCompletion.ts b/extensions/typescript-language-features/src/features/tagCompletion.ts index 0a01b398e7e..8121908e474 100644 --- a/extensions/typescript-language-features/src/features/tagCompletion.ts +++ b/extensions/typescript-language-features/src/features/tagCompletion.ts @@ -57,6 +57,11 @@ class TagClosing { return; } + const secondToLastCharacter = lastChange.text[lastChange.text.length - 2]; + if (secondToLastCharacter === '>') { + return; + } + const rangeStart = lastChange.range.start; const version = document.version; this.timeout = setTimeout(async () => { From 0178fc282482de90aac6fd16b253802593c29646 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 14:49:20 -0700 Subject: [PATCH 121/151] Make sure we clean up config change registration --- .../src/features/bufferSyncSupport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index b5560ee198e..ec6580b404e 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -156,7 +156,7 @@ export default class BufferSyncSupport { this.syncedBuffers = new SyncedBufferMap(path => this.normalizePath(path)); this.updateConfiguration(); - workspace.onDidChangeConfiguration(() => this.updateConfiguration(), null); + workspace.onDidChangeConfiguration(this.updateConfiguration, this, this.disposables); } private readonly _onDelete = new EventEmitter(); From d68d22f961f8596cb3a6e392d320fc6811235667 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 14:49:54 -0700 Subject: [PATCH 122/151] Pass in method directly instead of lambda for some events --- .../src/features/tagCompletion.ts | 6 +----- .../src/utils/dependentRegistration.ts | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/features/tagCompletion.ts b/extensions/typescript-language-features/src/features/tagCompletion.ts index 8121908e474..eecdc80aad5 100644 --- a/extensions/typescript-language-features/src/features/tagCompletion.ts +++ b/extensions/typescript-language-features/src/features/tagCompletion.ts @@ -120,12 +120,8 @@ export class ActiveDocumentDependentRegistration { register: () => vscode.Disposable, ) { this._registration = new ConditionalRegistration(register); - + vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); this.update(); - - vscode.window.onDidChangeActiveTextEditor(() => { - this.update(); - }, null, this._disposables); } public dispose() { diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 7ffe3c4195b..232d6b8e2cf 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -75,12 +75,8 @@ export class ConfigurationDependentRegistration { register: () => vscode.Disposable, ) { this._registration = new ConditionalRegistration(register); - this.update(); - - vscode.workspace.onDidChangeConfiguration(() => { - this.update(); - }, null, this._disposables); + vscode.workspace.onDidChangeConfiguration(this.update, this, this._disposables); } public dispose() { From faa5dfac4cf5c2f6ad0a70042397dc8b50a68619 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 15:03:44 -0700 Subject: [PATCH 123/151] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 951ff212d9c..60d3016b27d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.26.0", - "distro": "bf3aa85b8880d2d1c80030e21a3a2ddd384fa1e7", + "distro": "96ae9ca16a7c16ae63bafd206a92d4dc13754af2", "author": { "name": "Microsoft Corporation" }, From 0f699f35f2f2e0f36f6936fe5901bd2a974c4f8b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 15:31:10 -0700 Subject: [PATCH 124/151] Update js/ts grammar Addresses #53896 --- extensions/javascript/syntaxes/JavaScript.tmLanguage.json | 8 ++++++-- .../javascript/syntaxes/JavaScriptReact.tmLanguage.json | 8 ++++++-- .../typescript-basics/syntaxes/TypeScript.tmLanguage.json | 8 ++++++-- .../syntaxes/TypeScriptReact.tmLanguage.json | 8 ++++++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 8fd13accda3..4774b176fd2 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -139,6 +139,10 @@ "name": "keyword.control.with.js", "match": "(? Date: Tue, 10 Jul 2018 10:01:00 -0700 Subject: [PATCH 125/151] #53911 - render setting descriptions as markdown --- .../preferences/browser/media/settingsEditor2.css | 10 ++++++++-- .../parts/preferences/browser/settingsTree.ts | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index 8ac1386f601..ce0b8b96406 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -196,11 +196,18 @@ opacity: 0.7; margin-top: 3px; overflow: hidden; - white-space: pre; text-overflow: ellipsis; height: 18px; } +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description * { + margin: 0px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description code { + line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ +} + .settings-editor > .settings-body > .settings-tree-container .setting-measure-container.monaco-tree-row { position: absolute; visibility: hidden; @@ -209,7 +216,6 @@ .settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description, .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description { height: initial; - white-space: pre-wrap; } .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-value-description { diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 95a40dc3071..a90e339a32e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -30,6 +30,7 @@ import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; +import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; const $ = DOM.$; @@ -677,7 +678,10 @@ export class SettingsRenderer implements IRenderer { template.labelElement.textContent = element.displayLabel; template.labelElement.title = titleTooltip; - template.descriptionElement.textContent = element.description; + + const renderedDescription = renderMarkdown({ value: element.description }); + template.descriptionElement.innerHTML = ''; + template.descriptionElement.appendChild(renderedDescription); this.renderValue(element, isSelected, template); From d30631dea8882f9b979c6dd44c04dc81aeed256c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 10:08:32 -0700 Subject: [PATCH 126/151] Fix #53510 - remove relative pattern matching from file search --- src/vs/workbench/api/node/extHostSearch.ts | 42 ++------------ .../services/search/node/fileSearch.ts | 55 ++----------------- 2 files changed, 10 insertions(+), 87 deletions(-) diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index d35c2401007..3ce8be20002 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -13,7 +13,6 @@ import * as strings from 'vs/base/common/strings'; import URI, { UriComponents } from 'vs/base/common/uri'; import { PPromise, TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; -import * as pfs from 'vs/base/node/pfs'; import { IFileMatch, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; import * as vscode from 'vscode'; import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol'; @@ -30,9 +29,9 @@ export class ExtHostSearch implements ExtHostSearchShape { private _fileSearchManager: FileSearchManager; - constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs, private _pfs = pfs) { + constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs) { this._proxy = mainContext.getProxy(MainContext.MainThreadSearch); - this._fileSearchManager = new FileSearchManager(this._pfs); + this._fileSearchManager = new FileSearchManager(); } private _transformScheme(scheme: string): string { @@ -518,7 +517,7 @@ class FileSearchEngine { private globalExcludePattern: glob.ParsedExpression; - constructor(private config: ISearchQuery, private provider: vscode.SearchProvider, private _pfs: typeof pfs) { + constructor(private config: ISearchQuery, private provider: vscode.SearchProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; @@ -633,19 +632,6 @@ class FileSearchEngine { return null; } - if (noSiblingsClauses && this.isLimitHit) { - // If the limit was hit, check whether filePattern is an exact relative match because it must be included - return this.checkFilePatternRelativeMatch(fq.folder).then(({ exists, size }) => { - if (exists) { - onResult({ - base: fq.folder, - relativePath: this.filePattern, - basename: path.basename(this.filePattern), - }); - } - }); - } - this.matchDirectoryTree(tree, queryTester, onResult); return null; }).then( @@ -743,24 +729,6 @@ class FileSearchEngine { matchDirectory(rootEntries); } - private checkFilePatternRelativeMatch(base: URI): TPromise<{ exists: boolean, size?: number }> { - if (!this.filePattern || path.isAbsolute(this.filePattern) || base.scheme !== 'file') { - return TPromise.wrap({ exists: false }); - } - - const absolutePath = path.join(base.fsPath, this.filePattern); - return this._pfs.stat(absolutePath).then(stat => { - return { - exists: !stat.isDirectory(), - size: stat.size - }; - }, err => { - return { - exists: false - }; - }); - } - private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void { if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) { if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) { @@ -800,12 +768,10 @@ class FileSearchManager { private readonly expandedCacheKeys = new Map(); - constructor(private _pfs: typeof pfs) { } - fileSearch(config: ISearchQuery, provider: vscode.SearchProvider): PPromise { let searchP: PPromise; return new PPromise((c, e, p) => { - const engine = new FileSearchEngine(config, provider, this._pfs); + const engine = new FileSearchEngine(config, provider); searchP = this.doSearch(engine, FileSearchManager.BATCH_SIZE).then( result => { diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 1d6dc527cae..91834f3c67a 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -206,7 +206,6 @@ export class FileWalker { const useRipgrep = this.useRipgrep; let noSiblingsClauses: boolean; - let filePatternSeen = false; if (useRipgrep) { const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder).expression); cmd = ripgrep.cmd; @@ -265,9 +264,6 @@ export class FileWalker { if (useRipgrep && noSiblingsClauses) { for (const relativePath of relativeFiles) { - if (relativePath === this.filePattern) { - filePatternSeen = true; - } const basename = path.basename(relativePath); this.matchFile(onResult, { base: rootFolder, relativePath, basename }); if (this.isLimitHit) { @@ -276,22 +272,9 @@ export class FileWalker { } } if (last || this.isLimitHit) { - if (!filePatternSeen) { - this.checkFilePatternRelativeMatch(folderQuery.folder, (match, size) => { - if (match) { - this.resultCount++; - onResult({ - base: folderQuery.folder, - relativePath: this.filePattern, - basename: path.basename(this.filePattern), - }); - } - done(); - }); - } else { - done(); - } + done(); } + return; } @@ -518,25 +501,11 @@ export class FileWalker { return done(); } - // Support relative paths to files from a root resource (ignores excludes) - return this.checkFilePatternRelativeMatch(folderQuery.folder, (match, size) => { - if (this.isCanceled || this.isLimitHit) { - return done(); - } + if (this.isCanceled || this.isLimitHit) { + return done(); + } - // Report result from file pattern if matching - if (match) { - this.resultCount++; - onResult({ - base: folderQuery.folder, - relativePath: this.filePattern, - basename: path.basename(this.filePattern), - size - }); - } - - return this.doWalk(folderQuery, '', files, onResult, done); - }); + return this.doWalk(folderQuery, '', files, onResult, done); }); } @@ -556,18 +525,6 @@ export class FileWalker { }; } - private checkFilePatternRelativeMatch(basePath: string, clb: (matchPath: string, size?: number) => void): void { - if (!this.filePattern || path.isAbsolute(this.filePattern)) { - return clb(null); - } - - const absolutePath = path.join(basePath, this.filePattern); - - return fs.stat(absolutePath, (error, stat) => { - return clb(!error && !stat.isDirectory() ? absolutePath : null, stat && stat.size); // only existing files - }); - } - private doWalk(folderQuery: IFolderSearch, relativeParentPath: string, files: string[], onResult: (result: IRawFileMatch) => void, done: (error: Error) => void): void { const rootFolder = folderQuery.folder; From 3c19842c7f4faff87613ce55c650f15144a15e6c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 10:10:04 -0700 Subject: [PATCH 127/151] Fix #53840 - work around unlikely NPE --- .../workbench/parts/preferences/browser/settingsEditor2.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index 9e11c49536a..a033f68b8a5 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -693,7 +693,10 @@ export class SettingsEditor2 extends BaseEditor { // Count unique results const counts = {}; const filterResult = results[SearchResultIdx.Local]; - counts['filterResult'] = filterResult.filterMatches.length; + if (filterResult) { + counts['filterResult'] = filterResult.filterMatches.length; + } + if (nlpResult) { counts['nlpResult'] = nlpResult.filterMatches.length; } From e6c79967f4d680c2bd29abb3686cabe2610684a5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 10:12:55 -0700 Subject: [PATCH 128/151] Fix #53841 Not sure how this NPE could happen, but don't fail to render search results when it does --- src/vs/workbench/parts/search/browser/searchResultsView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index d30adb28b21..dd4b0933013 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -237,7 +237,8 @@ export class SearchRenderer extends Disposable implements IRenderer { private renderFolderMatch(tree: ITree, folderMatch: FolderMatch, templateData: IFolderMatchTemplate): void { if (folderMatch.hasRoot()) { - const fileKind = resources.isEqual(this.contextService.getWorkspaceFolder(folderMatch.resource()).uri, folderMatch.resource()) ? + const workspaceFolder = this.contextService.getWorkspaceFolder(folderMatch.resource()); + const fileKind = workspaceFolder && resources.isEqual(workspaceFolder.uri, folderMatch.resource()) ? FileKind.ROOT_FOLDER : FileKind.FOLDER; From c6e298d909a3a35d221d3576d511ace6ea7e9a17 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 11:36:43 -0700 Subject: [PATCH 129/151] renable disabled test. Seems fine now. --- .../src/singlefolder-tests/workspace.test.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 2e9fe0d0bbf..03548116a3b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -499,17 +499,16 @@ suite('workspace-namespace', () => { }); }); - // TODO@Joh this test fails randomly - // test('findFiles, cancellation', () => { + test('findFiles, cancellation', () => { - // const source = new CancellationTokenSource(); - // const token = source.token; // just to get an instance first - // source.cancel(); + const source = new vscode.CancellationTokenSource(); + const token = source.token; // just to get an instance first + source.cancel(); - // return vscode.workspace.findFiles('*.js', null, 100, token).then((res) => { - // assert.equal(res, void 0); - // }); - // }); + return vscode.workspace.findFiles('*.js', null, 100, token).then((res) => { + assert.deepEqual(res, []); + }); + }); test('findTextInFiles', async () => { const results: vscode.TextSearchResult[] = []; From 5bc87f70967c39eb242d26ca1036b89ad9f98f6b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 16:23:25 -0700 Subject: [PATCH 130/151] dead code --- src/vs/workbench/parts/search/browser/searchActions.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index bd2aa0463a9..50925449d9a 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -84,15 +84,6 @@ export const toggleRegexCommand = (accessor: ServicesAccessor) => { searchView.toggleRegex(); }; -export const FocusActiveEditorCommand = (accessor: ServicesAccessor) => { - const editorService = accessor.get(IEditorService); - const activeControl = editorService.activeControl; - if (activeControl) { - activeControl.focus(); - } - return TPromise.as(true); -}; - export class FocusNextInputAction extends Action { public static readonly ID = 'search.focus.nextInputBox'; From 13ce0e57cfd9caae496782779f0bf5e9b7c1e022 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 16:56:29 -0700 Subject: [PATCH 131/151] Settings editor - include missing "suggest" settings --- src/vs/workbench/parts/preferences/browser/settingsLayout.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts index caf7d55ff89..db7fd6c80ac 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts @@ -61,7 +61,7 @@ export const tocData: ITOCEntry = { { id: 'editor/suggestions', label: localize('suggestions', "Suggestions"), - settings: ['editor.*suggestion*'] + settings: ['editor.*suggest*'] }, { id: 'editor/files', From fea25ea84cbeb4bb441c60c703d6800063cb0fcd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 16:57:25 -0700 Subject: [PATCH 132/151] Settings editor - render enumDescriptions --- .../workbench/parts/preferences/browser/settingsTree.ts | 8 +++++++- .../workbench/services/preferences/common/preferences.ts | 1 + .../services/preferences/common/preferencesModels.ts | 5 +++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index a90e339a32e..3194a68332b 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -679,7 +679,13 @@ export class SettingsRenderer implements IRenderer { template.labelElement.textContent = element.displayLabel; template.labelElement.title = titleTooltip; - const renderedDescription = renderMarkdown({ value: element.description }); + const enumDescriptionText = element.setting.enumDescriptions ? + '\n' + element.setting.enumDescriptions + .map((desc, i) => ` - \`${element.setting.enum[i]}\`: ${desc}`) + .join('\n') : + ''; + const descriptionText = element.description + enumDescriptionText; + const renderedDescription = renderMarkdown({ value: descriptionText }); template.descriptionElement.innerHTML = ''; template.descriptionElement.appendChild(renderedDescription); diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 8c649215708..93be98d9f2b 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -48,6 +48,7 @@ export interface ISetting { // TODO@roblou maybe need new type and new EditorModel for GUI editor instead of ISetting which is used for text settings editor type?: string | string[]; enum?: string[]; + enumDescriptions?: string[]; } export interface IExtensionSetting extends ISetting { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 4f3ca88dfe0..304a0794663 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -473,7 +473,8 @@ export class DefaultSettings extends Disposable { valueRange: null, overrides: [], type: setting.type, - enum: setting.enum + enum: setting.enum, + enumDescriptions: setting.enumDescriptions }; } return null; @@ -547,7 +548,7 @@ export class DefaultSettings extends Disposable { const value = prop.default; const description = (prop.description || '').split('\n'); const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; - result.push({ key, value, description, range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides, type: prop.type, enum: prop.enum }); + result.push({ key, value, description, range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides, type: prop.type, enum: prop.enum, enumDescriptions: prop.enumDescriptions }); } } return result; From 643f65f34ef28a098cbb46c31585315abd875bff Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 17:02:15 -0700 Subject: [PATCH 133/151] Settings editor - fix clicks on the setting header or descritption stealing focus --- .../workbench/parts/preferences/browser/settingsTree.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 3194a68332b..cef267c5f89 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -31,6 +31,7 @@ import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferenc import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; const $ = DOM.$; @@ -852,6 +853,14 @@ export class SettingsTreeController extends WorkbenchTreeController { ) { super({}, configurationService); } + + protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin?: string): boolean { + // Without this, clicking on the setting description causes the tree to lose focus. I don't know why. + // The superclass does not always call it because of DND which is not used here. + eventish.preventDefault(); + + return super.onLeftClick(tree, element, eventish, origin); + } } export class SettingsAccessibilityProvider implements IAccessibilityProvider { From 585c65041171ff14710801855ce62be2a8844b92 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 15:55:20 -0700 Subject: [PATCH 134/151] Remove cyle around requestDiagnostic --- .../src/features/bufferSyncSupport.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index ec6580b404e..f267c73bd10 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -18,10 +18,6 @@ enum BufferKind { JavaScript = 2, } -interface IDiagnosticRequestor { - requestDiagnostic(resource: Uri): void; -} - function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined { switch (mode) { case languageModeIds.typescript: return 'TS'; @@ -37,7 +33,6 @@ class SyncedBuffer { constructor( private readonly document: TextDocument, public readonly filepath: string, - private readonly diagnosticRequestor: IDiagnosticRequestor, private readonly client: ITypeScriptServiceClient ) { } @@ -110,7 +105,6 @@ class SyncedBuffer { }; this.client.execute('change', args, false); } - this.diagnosticRequestor.requestDiagnostic(this.document.uri); } } @@ -210,7 +204,7 @@ export default class BufferSyncSupport { return; } - const syncedBuffer = new SyncedBuffer(document, filepath, this, this.client); + const syncedBuffer = new SyncedBuffer(document, filepath, this.client); this.syncedBuffers.set(resource, syncedBuffer); syncedBuffer.open(); this.requestDiagnostic(resource); @@ -240,6 +234,8 @@ export default class BufferSyncSupport { } syncedBuffer.onContentChanged(e.contentChanges); + this.requestDiagnostic(syncedBuffer.resource); + if (this.pendingGetErr) { this.pendingGetErr.token.cancel(); this.pendingGetErr = undefined; From bc627a5a830d8aa2e1e5a51815b89d82eee2a303 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 15:59:58 -0700 Subject: [PATCH 135/151] Remove duplicate code for creating FormattingRequestArgs --- .../src/features/bufferSyncSupport.ts | 9 +++------ .../src/features/formatting.ts | 12 +++--------- .../src/utils/typeConverters.ts | 8 ++++++++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index f267c73bd10..2d970d1cf4b 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -11,6 +11,7 @@ import API from '../utils/api'; import { Delayer } from '../utils/async'; import { disposeAll } from '../utils/dispose'; import * as languageModeIds from '../utils/languageModeIds'; +import * as typeConverters from '../utils/typeConverters'; import { ResourceMap } from './resourceMap'; enum BufferKind { @@ -96,12 +97,8 @@ class SyncedBuffer { public onContentChanged(events: TextDocumentContentChangeEvent[]): void { for (const { range, text } of events) { const args: Proto.ChangeRequestArgs = { - file: this.filepath, - line: range.start.line + 1, - offset: range.start.character + 1, - endLine: range.end.line + 1, - endOffset: range.end.character + 1, - insertString: text + insertString: text, + ...typeConverters.Range.toFormattingRequestArgs(this.filepath, range) }; this.client.execute('change', args, false); } diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index e0556c99ec8..029db28f870 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -51,17 +51,11 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise { - const absPath = this.client.toPath(document.uri); - if (!absPath) { + const file = this.client.toPath(document.uri); + if (!file) { return []; } - const args: Proto.FormatRequestArgs = { - file: absPath, - line: range.start.line + 1, - offset: range.start.character + 1, - endLine: range.end.line + 1, - endOffset: range.end.character + 1 - }; + const args = typeConverters.Range.toFormattingRequestArgs(file, range); return this.doFormat(document, options, args, token); } diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index 8c87338893d..44ad7f4ae5e 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -24,6 +24,14 @@ export namespace Range { endLine: range.end.line + 1, endOffset: range.end.character + 1 }); + + export const toFormattingRequestArgs = (file: string, range: vscode.Range): Proto.FormatRequestArgs => ({ + file, + line: range.start.line + 1, + offset: range.start.character + 1, + endLine: range.end.line + 1, + endOffset: range.end.character + 1 + }); } export namespace Position { From 5a6e122a583ea8567a1ff95dfae449c0350b095c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 16:07:52 -0700 Subject: [PATCH 136/151] Extract PendingDiagnostics to own class --- .../src/features/bufferSyncSupport.ts | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 2d970d1cf4b..93bf5904448 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -120,6 +120,28 @@ class SyncedBufferMap extends ResourceMap { } } +class PendingDiagnostics { + private readonly _pendingDiagnostics = new Map(); + + public set(file: string, time: number): void { + this._pendingDiagnostics.set(file, time); + } + + public has(file: string): boolean { + return this._pendingDiagnostics.has(file); + } + + public clear(): void { + this._pendingDiagnostics.clear(); + } + + public getFileList(): Set { + return new Set(Array.from(this._pendingDiagnostics.entries()) + .sort((a, b) => a[1] - b[1]) + .map(entry => entry[0])); + } +} + export default class BufferSyncSupport { private readonly client: ITypeScriptServiceClient; @@ -130,7 +152,7 @@ export default class BufferSyncSupport { private readonly disposables: Disposable[] = []; private readonly syncedBuffers: SyncedBufferMap; - private readonly pendingDiagnostics = new Map(); + private readonly pendingDiagnostics = new PendingDiagnostics(); private readonly diagnosticDelayer: Delayer; private pendingGetErr: { request: Promise, files: string[], token: CancellationTokenSource } | undefined; private listening: boolean = false; @@ -298,28 +320,26 @@ export default class BufferSyncSupport { } private sendPendingDiagnostics(): void { - const files = new Set(Array.from(this.pendingDiagnostics.entries()) - .sort((a, b) => a[1] - b[1]) - .map(entry => entry[0])); + const fileList = this.pendingDiagnostics.getFileList(); // Add all open TS buffers to the geterr request. They might be visible for (const file of this.syncedBuffers.allResources) { - if (!this.pendingDiagnostics.get(file)) { - files.add(file); + if (!this.pendingDiagnostics.has(file)) { + fileList.add(file); } } if (this.pendingGetErr) { for (const file of this.pendingGetErr.files) { - files.add(file); + fileList.add(file); } } - if (files.size) { - const fileList = Array.from(files); + if (fileList.size) { + const files = Array.from(fileList); const args: Proto.GeterrRequestArgs = { delay: 0, - files: fileList + files }; const token = new CancellationTokenSource(); @@ -331,7 +351,7 @@ export default class BufferSyncSupport { this.pendingGetErr = undefined; } }), - files: fileList, + files, token }; } From 2adafad5ae016d263f27ae4df4120fe067f72fa5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 16:12:32 -0700 Subject: [PATCH 137/151] Extract duplicated triggerDiagnostics code to function --- .../src/features/bufferSyncSupport.ts | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 93bf5904448..d6da2443f3f 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -259,9 +259,7 @@ export default class BufferSyncSupport { this.pendingGetErr.token.cancel(); this.pendingGetErr = undefined; - this.diagnosticDelayer.trigger(() => { - this.sendPendingDiagnostics(); - }, 200); + this.triggerDiagnostics(); } } @@ -271,9 +269,7 @@ export default class BufferSyncSupport { this.pendingDiagnostics.set(buffer.filepath, Date.now()); } } - this.diagnosticDelayer.trigger(() => { - this.sendPendingDiagnostics(); - }, 200); + this.triggerDiagnostics(); } public getErr(resources: Uri[]): any { @@ -289,9 +285,13 @@ export default class BufferSyncSupport { } } + this.triggerDiagnostics(); + } + + private triggerDiagnostics(delay: number = 200) { this.diagnosticDelayer.trigger(() => { this.sendPendingDiagnostics(); - }, 200); + }, delay); } public requestDiagnostic(resource: Uri): void { @@ -306,12 +306,9 @@ export default class BufferSyncSupport { return; } - let delay = 300; const lineCount = buffer.lineCount; - delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800); - this.diagnosticDelayer.trigger(() => { - this.sendPendingDiagnostics(); - }, delay); + const delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800); + this.triggerDiagnostics(delay); } public hasPendingDiagnostics(resource: Uri): boolean { From 84100f37c6b51933c09736a9f2f5f2a6e5572934 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 16:20:40 -0700 Subject: [PATCH 138/151] Request diagnostics on synced buffer --- .../src/features/bufferSyncSupport.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index d6da2443f3f..81be821dee5 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -226,7 +226,7 @@ export default class BufferSyncSupport { const syncedBuffer = new SyncedBuffer(document, filepath, this.client); this.syncedBuffers.set(resource, syncedBuffer); syncedBuffer.open(); - this.requestDiagnostic(resource); + this.requestDiagnostic(syncedBuffer); } public closeResource(resource: Uri): void { @@ -253,7 +253,7 @@ export default class BufferSyncSupport { } syncedBuffer.onContentChanged(e.contentChanges); - this.requestDiagnostic(syncedBuffer.resource); + this.requestDiagnostic(syncedBuffer); if (this.pendingGetErr) { this.pendingGetErr.token.cancel(); @@ -294,17 +294,12 @@ export default class BufferSyncSupport { }, delay); } - public requestDiagnostic(resource: Uri): void { - const file = this.client.normalizedPath(resource); - if (!file) { + private requestDiagnostic(buffer: SyncedBuffer): void { + if (!this.shouldValidate(buffer)) { return; } - this.pendingDiagnostics.set(file, Date.now()); - const buffer = this.syncedBuffers.get(resource); - if (!buffer || !this.shouldValidate(buffer)) { - return; - } + this.pendingDiagnostics.set(buffer.filepath, Date.now()); const lineCount = buffer.lineCount; const delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800); From f5e7c01028b8b04120a32ef3c545566ee93c9f7e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 16:43:57 -0700 Subject: [PATCH 139/151] Use resource map for pending diagnostics --- .../src/features/bufferSyncSupport.ts | 48 ++++++------------- .../src/features/resourceMap.ts | 8 +++- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 81be821dee5..bd622252378 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -114,29 +114,11 @@ class SyncedBufferMap extends ResourceMap { public get allBuffers(): Iterable { return this.values; } - - public get allResources(): Iterable { - return this.keys; - } } -class PendingDiagnostics { - private readonly _pendingDiagnostics = new Map(); - - public set(file: string, time: number): void { - this._pendingDiagnostics.set(file, time); - } - - public has(file: string): boolean { - return this._pendingDiagnostics.has(file); - } - - public clear(): void { - this._pendingDiagnostics.clear(); - } - +class PendingDiagnostics extends ResourceMap { public getFileList(): Set { - return new Set(Array.from(this._pendingDiagnostics.entries()) + return new Set(Array.from(this.entries) .sort((a, b) => a[1] - b[1]) .map(entry => entry[0])); } @@ -151,8 +133,7 @@ export default class BufferSyncSupport { private readonly modeIds: Set; private readonly disposables: Disposable[] = []; private readonly syncedBuffers: SyncedBufferMap; - - private readonly pendingDiagnostics = new PendingDiagnostics(); + private readonly pendingDiagnostics: PendingDiagnostics; private readonly diagnosticDelayer: Delayer; private pendingGetErr: { request: Promise, files: string[], token: CancellationTokenSource } | undefined; private listening: boolean = false; @@ -166,7 +147,9 @@ export default class BufferSyncSupport { this.diagnosticDelayer = new Delayer(300); - this.syncedBuffers = new SyncedBufferMap(path => this.normalizePath(path)); + const pathNormalizer = (path: Uri) => this.normalizePath(path); + this.syncedBuffers = new SyncedBufferMap(pathNormalizer); + this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); this.updateConfiguration(); workspace.onDidChangeConfiguration(this.updateConfiguration, this, this.disposables); @@ -259,6 +242,7 @@ export default class BufferSyncSupport { this.pendingGetErr.token.cancel(); this.pendingGetErr = undefined; + // In this case we always want to re-trigger all diagnostics this.triggerDiagnostics(); } } @@ -266,7 +250,7 @@ export default class BufferSyncSupport { public requestAllDiagnostics() { for (const buffer of this.syncedBuffers.allBuffers) { if (this.shouldValidate(buffer)) { - this.pendingDiagnostics.set(buffer.filepath, Date.now()); + this.pendingDiagnostics.set(buffer.resource, Date.now()); } } this.triggerDiagnostics(); @@ -279,10 +263,7 @@ export default class BufferSyncSupport { } for (const resource of handledResources) { - const file = this.client.normalizedPath(resource); - if (file) { - this.pendingDiagnostics.set(file, Date.now()); - } + this.pendingDiagnostics.set(resource, Date.now()); } this.triggerDiagnostics(); @@ -299,7 +280,7 @@ export default class BufferSyncSupport { return; } - this.pendingDiagnostics.set(buffer.filepath, Date.now()); + this.pendingDiagnostics.set(buffer.resource, Date.now()); const lineCount = buffer.lineCount; const delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800); @@ -307,17 +288,16 @@ export default class BufferSyncSupport { } public hasPendingDiagnostics(resource: Uri): boolean { - const file = this.client.normalizedPath(resource); - return !file || this.pendingDiagnostics.has(file); + return this.pendingDiagnostics.has(resource); } private sendPendingDiagnostics(): void { const fileList = this.pendingDiagnostics.getFileList(); // Add all open TS buffers to the geterr request. They might be visible - for (const file of this.syncedBuffers.allResources) { - if (!this.pendingDiagnostics.has(file)) { - fileList.add(file); + for (const buffer of this.syncedBuffers.values) { + if (!this.pendingDiagnostics.has(buffer.resource)) { + fileList.add(buffer.filepath); } } diff --git a/extensions/typescript-language-features/src/features/resourceMap.ts b/extensions/typescript-language-features/src/features/resourceMap.ts index f2b9c6fd711..73d89182315 100644 --- a/extensions/typescript-language-features/src/features/resourceMap.ts +++ b/extensions/typescript-language-features/src/features/resourceMap.ts @@ -45,12 +45,16 @@ export class ResourceMap { } } + public clear() { + this._map.clear(); + } + public get values(): Iterable { return this._map.values(); } - public get keys(): Iterable { - return this._map.keys(); + public get entries() { + return this._map.entries(); } private toKey(resource: Uri): string | null { From 860138b2b2dcf0cf91c5eb09ab7474112c91f876 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 16:47:03 -0700 Subject: [PATCH 140/151] Remove normalizePath from BufferSyncSupport --- .../src/features/bufferSyncSupport.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index bd622252378..d8ade29a919 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -147,7 +147,7 @@ export default class BufferSyncSupport { this.diagnosticDelayer = new Delayer(300); - const pathNormalizer = (path: Uri) => this.normalizePath(path); + const pathNormalizer = (path: Uri) => this.client.normalizedPath(path); this.syncedBuffers = new SyncedBufferMap(pathNormalizer); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); @@ -348,8 +348,4 @@ export default class BufferSyncSupport { return this._validateTypeScript; } } - - private normalizePath(path: Uri): string | null { - return this.client.normalizedPath(path); - } } From 5271932ab95f5aa5c2af18c70ebff8aa3da5d659 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Jul 2018 17:13:45 -0700 Subject: [PATCH 141/151] Fix reference search Fix #53902 --- src/vs/editor/contrib/referenceSearch/referenceSearch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts index cb21013f62b..849b2a3d102 100644 --- a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts +++ b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts @@ -138,7 +138,7 @@ let showReferencesCommand: ICommandHandler = (accessor: ServicesAccessor, resour return TPromise.as(controller.toggleWidget( new Range(position.lineNumber, position.column, position.lineNumber, position.column), - createCancelablePromise(_ => Promise.reject(new ReferencesModel(references))), + createCancelablePromise(_ => Promise.resolve(new ReferencesModel(references))), defaultReferenceSearchOptions)).then(() => true); }); }; From 6a72f3fdc72a3a923c7db9b284d702852a7a6599 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 17:22:32 -0700 Subject: [PATCH 142/151] Remove obsolete test --- .../services/search/test/node/search.test.ts | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index 1d19c3d0c76..fda8eed27b0 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -600,29 +600,6 @@ suite('FileSearchEngine', () => { }); }); - test('Files: relative path to file ignores excludes', function (done: () => void) { - this.timeout(testTimeout); - let engine = new FileSearchEngine({ - folderQueries: ROOT_FOLDER_QUERY, - filePattern: path.normalize(path.join('examples', 'company.js')), - excludePattern: { '**/*.js': true } - }); - - let count = 0; - let res: IRawFileMatch; - engine.search((result) => { - if (result) { - count++; - } - res = result; - }, () => { }, (error) => { - assert.ok(!error); - assert.equal(count, 1); - assert.equal(path.basename(res.relativePath), 'company.js'); - done(); - }); - }); - test('Files: Include pattern, single files', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ From 215b883178fb88c8ac18d6b58a8c567a6ac6840b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 18:55:36 -0700 Subject: [PATCH 143/151] Settings editor - allow linking to other settings from the setting description --- .../common/config/commonEditorConfig.ts | 8 ++-- .../electron-browser/files.contribution.ts | 4 +- .../preferences/browser/settingsEditor2.ts | 12 +++++- .../parts/preferences/browser/settingsTree.ts | 42 ++++++++++++++++--- .../electron-browser/search.contribution.ts | 2 +- 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 9774ab0405b..556665f4a6e 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -282,19 +282,19 @@ const editorConfiguration: IConfigurationNode = { 'type': 'number', 'default': EDITOR_MODEL_DEFAULTS.tabSize, 'minimum': 1, - 'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `editor.detectIndentation` is on."), + 'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when [`editor.detectIndentation`](#editor.detectIndentation) is on."), 'errorMessage': nls.localize('tabSize.errorMessage', "Expected 'number'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.") }, 'editor.insertSpaces': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.insertSpaces, - 'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overridden based on the file contents when `editor.detectIndentation` is on."), + 'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overridden based on the file contents when [`editor.detectIndentation`](#editor.detectIndentation) is on."), 'errorMessage': nls.localize('insertSpaces.errorMessage', "Expected 'boolean'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.") }, 'editor.detectIndentation': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.detectIndentation, - 'description': nls.localize('detectIndentation', "When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents.") + 'description': nls.localize('detectIndentation', "When opening a file, [`editor.tabSize`](#editor.tabSize) and [`editor.insertSpaces`](#editor.insertSpaces) will be detected based on the file contents.") }, 'editor.roundedSelection': { 'type': 'boolean', @@ -440,7 +440,7 @@ const editorConfiguration: IConfigurationNode = { '- `ctrlCmd` refers to a value the setting can take and should not be localized.', '- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.' ] - }, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier.") + }, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier)") }, 'editor.multiCursorMergeOverlapping': { 'type': 'boolean', diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index dfe8322c5ae..6ab2e4e0a4d 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -251,12 +251,12 @@ configurationRegistry.registerConfiguration({ nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onWindowChange' }, "A dirty file is automatically saved when the window loses focus.") ], 'default': AutoSaveConfiguration.OFF, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in 'files.autoSaveDelay'.", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in [files.autoSaveDelay](#files.autoSaveDelay). Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) }, 'files.autoSaveDelay': { 'type': 'number', 'default': 1000, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when 'files.autoSave' is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY) + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when [`files.autoSave`](#files.autoSave) is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY) }, 'files.watcherExclude': { 'type': 'object', diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index a033f68b8a5..b53ca7d6986 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -33,7 +33,7 @@ import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbenc import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, NonExpandableTree, resolveExtensionsSettings, resolveSettingsTree, SearchResultIdx, SearchResultModel, SettingsAccessibilityProvider, SettingsDataSource, SettingsRenderer, SettingsTreeController, SettingsTreeElement, SettingsTreeFilter, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; import { TOCDataSource, TOCRenderer, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_FIRST_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_TOC_ROW_FOCUS } from 'vs/workbench/parts/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_FIRST_ROW_FOCUS, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; import { IPreferencesService, ISearchResult, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; @@ -234,6 +234,15 @@ export class SettingsEditor2 extends BaseEditor { this._register(DOM.addDisposableListener(this.showConfiguredSettingsOnlyCheckbox, 'change', e => this.onShowConfiguredOnlyClicked())); } + private revealSetting(settingName: string): void { + const element = this.settingsTreeModel.getElementByName(settingName); + if (element) { + this.settingsTree.setSelection([element]); + this.settingsTree.setFocus(element); + this.settingsTree.reveal(element); + } + } + private openSettingsFile(): TPromise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; @@ -314,6 +323,7 @@ export class SettingsEditor2 extends BaseEditor { const renderer = this.instantiationService.createInstance(SettingsRenderer, this.settingsTreeContainer); this._register(renderer.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value))); this._register(renderer.onDidOpenSettings(() => this.openSettingsFile())); + this._register(renderer.onDidClickSettingLink(settingName => this.revealSetting(settingName))); const treeClass = 'settings-editor-tree'; this.settingsTree = this.instantiationService.createInstance(NonExpandableTree, this.settingsTreeContainer, diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index cef267c5f89..b41fcc68144 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Button } from 'vs/base/browser/ui/button/button'; @@ -12,11 +13,12 @@ import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import * as arrays from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; -import { escapeRegExpCharacters } from 'vs/base/common/strings'; +import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IAccessibilityProvider, IDataSource, IFilter, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; @@ -24,14 +26,13 @@ import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; import { registerColor, selectBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; -import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; const $ = DOM.$; @@ -99,6 +100,7 @@ export interface ITOCEntry { export class SettingsTreeModel { private _root: SettingsTreeGroupElement; private _treeElementsById = new Map(); + private _treeElementsBySettingName = new Map(); constructor( private _viewState: ISettingsEditorViewState, @@ -127,6 +129,10 @@ export class SettingsTreeModel { return this._treeElementsById.get(id); } + getElementByName(name: string): SettingsTreeElement { + return this._treeElementsBySettingName.get(name); + } + private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { const element = new SettingsTreeGroupElement(); element.id = tocEntry.id; @@ -155,6 +161,7 @@ export class SettingsTreeModel { private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement { const element = createSettingsTreeSettingElement(setting, parent, this._viewState.settingsTarget, this._configurationService); this._treeElementsById.set(element.id, element); + this._treeElementsBySettingName.set(setting.key, element); return element; } } @@ -446,12 +453,16 @@ export class SettingsRenderer implements IRenderer { private readonly _onDidOpenSettings: Emitter = new Emitter(); public readonly onDidOpenSettings: Event = this._onDidOpenSettings.event; + private readonly _onDidClickSettingLink: Emitter = new Emitter(); + public readonly onDidClickSettingLink: Event = this._onDidClickSettingLink.event; + private measureContainer: HTMLElement; constructor( _measureContainer: HTMLElement, @IThemeService private themeService: IThemeService, - @IContextViewService private contextViewService: IContextViewService + @IContextViewService private contextViewService: IContextViewService, + @IOpenerService private readonly openerService: IOpenerService, ) { this.measureContainer = DOM.append(_measureContainer, $('.setting-measure-container.monaco-tree-row')); } @@ -686,7 +697,19 @@ export class SettingsRenderer implements IRenderer { .join('\n') : ''; const descriptionText = element.description + enumDescriptionText; - const renderedDescription = renderMarkdown({ value: descriptionText }); + const renderedDescription = renderMarkdown({ value: descriptionText }, { + actionHandler: { + callback: (content: string) => { + if (startsWith(content, '#')) { + this._onDidClickSettingLink.fire(content.substr(1)); + } else { + this.openerService.open(URI.parse(content)).then(void 0, onUnexpectedError); + } + }, + disposeables: template.toDispose + } + }); + renderedDescription.classList.add('setting-item-description-markdown'); template.descriptionElement.innerHTML = ''; template.descriptionElement.appendChild(renderedDescription); @@ -854,7 +877,14 @@ export class SettingsTreeController extends WorkbenchTreeController { super({}, configurationService); } - protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin?: string): boolean { + protected onLeftClick(tree: ITree, element: any, eventish: IMouseEvent, origin?: string): boolean { + const isLink = eventish.target.tagName.toLowerCase() === 'a' || + eventish.target.parentElement.tagName.toLowerCase() === 'a'; // inside + + if (isLink && DOM.findParentWithClass(eventish.target, 'setting-item-description-markdown', tree.getHTMLElement())) { + return true; + } + // Without this, clicking on the setting description causes the tree to lose focus. I don't know why. // The superclass does not always call it because of DND which is not used here. eventish.preventDefault(); diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index cfef04d170f..bff2ceb88c9 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -531,7 +531,7 @@ configurationRegistry.registerConfiguration({ properties: { 'search.exclude': { type: 'object', - description: nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the files.exclude setting."), + description: nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the [`files.exclude`](#files-exclude) setting. Read more about glob patterns [here](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options)."), default: { '**/node_modules': true, '**/bower_components': true }, additionalProperties: { anyOf: [ From d5e22945a365608ca6e6375693ba0db4f2275e9e Mon Sep 17 00:00:00 2001 From: Christopher Leidigh Date: Tue, 10 Jul 2018 22:38:02 -0400 Subject: [PATCH 144/151] Only fire onDidSelect if user change: Fixes: #52632 (#53627) --- .../browser/ui/selectBox/selectBoxCustom.ts | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 249abc20629..0c80b4f4b92 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -242,6 +242,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate= 0) { + if (this.selected !== this._currentSelection) { + // Reset selected to current if no change this.select(this._currentSelection); } - this._onDidSelect.fire({ - index: this.selectElement.selectedIndex, - selected: this.selectElement.title - }); - this.hideSelectDropDown(false); } // List keyboard controller - // List exit - active - hide ContextView dropdown, return focus to parent select, fire onDidSelect + + // List exit - active - hide ContextView dropdown, reset selection, return focus to parent select private onEscape(e: StandardKeyboardEvent): void { dom.EventHelper.stop(e); + + // Reset selection to value when opened this.select(this._currentSelection); - this.hideSelectDropDown(true); - - this._onDidSelect.fire({ - index: this.selectElement.selectedIndex, - selected: this.selectElement.title - }); } - // List exit - active - hide ContextView dropdown, return focus to parent select, fire onDidSelect + // List exit - active - hide ContextView dropdown, return focus to parent select, fire onDidSelect if change private onEnter(e: StandardKeyboardEvent): void { dom.EventHelper.stop(e); - // Reset current selection - this._currentSelection = -1; + // Only fire if selection change + if (this.selected !== this._currentSelection) { + this._currentSelection = this.selected; + this._onDidSelect.fire({ + index: this.selectElement.selectedIndex, + selected: this.selectElement.title + }); + } this.hideSelectDropDown(true); - this._onDidSelect.fire({ - index: this.selectElement.selectedIndex, - selected: this.selectElement.title - }); } // List navigation - have to handle a disabled option (jump over) From cdfd58c03b1598e9f3ce23a4b4313d76284e5edf Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 10 Jul 2018 21:38:49 -0700 Subject: [PATCH 145/151] Settings editor - description tweaks --- .../parts/files/electron-browser/files.contribution.ts | 2 +- .../parts/preferences/browser/media/settingsEditor2.css | 1 + src/vs/workbench/parts/preferences/browser/settingsEditor2.ts | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index 6ab2e4e0a4d..c152f150f4d 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -251,7 +251,7 @@ configurationRegistry.registerConfiguration({ nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onWindowChange' }, "A dirty file is automatically saved when the window loses focus.") ], 'default': AutoSaveConfiguration.OFF, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in [files.autoSaveDelay](#files.autoSaveDelay). Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in [`files.autoSaveDelay`](#files.autoSaveDelay). Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) }, 'files.autoSaveDelay': { 'type': 'number', diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index ce0b8b96406..05d96d70d32 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -9,6 +9,7 @@ .settings-editor { padding-top: 11px; + padding-left: 2px; max-width: 1100px; margin: auto; } diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index b53ca7d6986..8ae13de64d4 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -239,7 +239,8 @@ export class SettingsEditor2 extends BaseEditor { if (element) { this.settingsTree.setSelection([element]); this.settingsTree.setFocus(element); - this.settingsTree.reveal(element); + this.settingsTree.reveal(element, 0); + this.settingsTree.domFocus(); } } From 2b79b107b0ef15c9942c2fae2b44dff016195dfe Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Jul 2018 07:31:30 +0200 Subject: [PATCH 146/151] remove dead branch --- src/vs/workbench/api/node/extHostProgress.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/node/extHostProgress.ts b/src/vs/workbench/api/node/extHostProgress.ts index fc4be5b66e6..f4a17349909 100644 --- a/src/vs/workbench/api/node/extHostProgress.ts +++ b/src/vs/workbench/api/node/extHostProgress.ts @@ -71,11 +71,10 @@ export class ExtHostProgress implements ExtHostProgressShape { function mergeProgress(result: IProgressStep, currentValue: IProgressStep): IProgressStep { result.message = currentValue.message; - if (typeof currentValue.increment === 'number' && typeof result.message === 'number') { - result.increment += currentValue.increment; - } else if (typeof currentValue.increment === 'number') { + if (typeof currentValue.increment === 'number') { result.increment = currentValue.increment; } + return result; } From bd0ce761a3038190c128a83ca260890ee40d9a40 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Jul 2018 09:49:37 +0200 Subject: [PATCH 147/151] escape newlines in outline item labels --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index e8f7c123321..3f449830417 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -91,7 +91,7 @@ class Item extends BreadcrumbsItem { } else if (this.element instanceof OutlineElement) { // symbol let label = new IconLabel(container); - label.setValue(this.element.symbol.name); + label.setValue(this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE')); this._disposables.push(label); } } From ca5219040c4720b254bb3effac5efd521a72cc0f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Jul 2018 10:39:36 +0200 Subject: [PATCH 148/151] enfore contextView-styles that can get lost due to `monaco-workbench`-style --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 3f449830417..203fb414cfb 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -226,6 +226,9 @@ export class BreadcrumbsControl { const color = theme.getColor(editorBackground).darken(theme.type === 'dark' ? .2 : .1); container.style.borderColor = color.toString(); container.style.boxShadow = `2px 2px 3px ${color.toString()}`; + container.style.position = 'absolute'; + container.style.zIndex = '1000'; + let { element } = event.item as Item; let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; let res = this._instantiationService.createInstance(ctor, container, element); From a81119c3611cd02d9ebb5dcc8de8d1000a8da718 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Jul 2018 10:39:59 +0200 Subject: [PATCH 149/151] correct icon height style --- src/vs/workbench/browser/parts/editor/media/breadcrumbs.css | 4 ++++ .../browser/parts/editor/media/notabstitlecontrol.css | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css index fe1ca4cedba..64d3d29c4b6 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbs.css @@ -20,3 +20,7 @@ border-style: solid; border-width: 1px; } + +.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-icon-label::before { + height: 18px; /* tweak the icon size of the editor labels when icons are enabled */ +} diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 7a2683b4f03..b81637b0a06 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -5,7 +5,7 @@ /* Title Label */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container .title-label { line-height: 35px; overflow: hidden; text-overflow: ellipsis; @@ -13,7 +13,7 @@ padding-left: 20px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container .monaco-icon-label::before { height: 35px; /* tweak the icon size of the editor labels when icons are enabled */ } @@ -26,4 +26,4 @@ .monaco-workbench > .part.editor > .content .editor-group-container.active > .title .title-actions { opacity: 1; -} \ No newline at end of file +} From c5b902b16984cc24268bab1e96334c605ad4428b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Jul 2018 10:50:12 +0200 Subject: [PATCH 150/151] don't pollute context view dom node --- .../workbench/browser/parts/editor/breadcrumbsControl.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 203fb414cfb..6990ff52921 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -220,14 +220,16 @@ export class BreadcrumbsControl { getAnchor() { return event.node; }, - render: (container: HTMLElement) => { - dom.addClasses(container, 'monaco-breadcrumbs-picker', 'monaco-workbench', 'show-file-icons'); + render: (parent: HTMLElement) => { + const container = document.createElement('div'); + parent.appendChild(container); const theme = this._themeService.getTheme(); const color = theme.getColor(editorBackground).darken(theme.type === 'dark' ? .2 : .1); container.style.borderColor = color.toString(); container.style.boxShadow = `2px 2px 3px ${color.toString()}`; container.style.position = 'absolute'; container.style.zIndex = '1000'; + dom.addClasses(container, 'monaco-breadcrumbs-picker', 'monaco-workbench', 'show-file-icons'); let { element } = event.item as Item; let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; @@ -303,8 +305,8 @@ export abstract class BreadcrumbsPicker { this.focus = dom.trackFocus(this._domNode); this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); this._tree.setInput(this._getInput(input)).then(_ => { - this._tree.domFocus(); this._tree.focusFirst(); + this._tree.domFocus(); }, onUnexpectedError); } From 9eed76230662c17cf18ac52b7a184d542bf68f09 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 11 Jul 2018 10:48:51 +0200 Subject: [PATCH 151/151] urgent build fix --- build/tfs/product-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index 2f2dce5b94c..570925d6c99 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -3,7 +3,7 @@ phases: condition: eq(variables['VSCODE_BUILD_WIN32'], 'true') queue: Hosted VS2017 variables: - VSCODE_ARCH: ia32 + VSCODE_ARCH: x64 steps: - template: win32/product-build-win32.yml