Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

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

View File

@@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/tree';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom';
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree';
import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent, TreeMouseEventTarget } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd';
import { range, equals, distinctES6 } from 'vs/base/common/arrays';
@@ -25,6 +25,7 @@ import { isMacintosh } from 'vs/base/common/platform';
import { values } from 'vs/base/common/map';
import { clamp } from 'vs/base/common/numbers';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { SetMap } from 'vs/base/common/collections';
function asTreeDragAndDropData<T, TFilterData>(data: IDragAndDropData): IDragAndDropData {
if (data instanceof ElementsDragAndDropData) {
@@ -152,7 +153,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
}
},
enableKeyboardNavigation: options.simpleKeyboardNavigation,
ariaSetProvider: {
ariaProvider: {
getSetSize(node) {
return node.parent!.visibleChildrenCount;
},
@@ -188,12 +189,48 @@ export class ComposedTreeDelegate<T, N extends { element: T }> implements IListV
interface ITreeListTemplateData<T> {
readonly container: HTMLElement;
readonly indent: HTMLElement;
readonly twistie: HTMLElement;
indentGuidesDisposable: IDisposable;
readonly templateData: T;
}
export enum RenderIndentGuides {
None = 'none',
OnHover = 'onHover',
Always = 'always'
}
interface ITreeRendererOptions {
readonly indent?: number;
readonly renderIndentGuides?: RenderIndentGuides;
}
interface IRenderData<TTemplateData> {
templateData: ITreeListTemplateData<TTemplateData>;
height: number;
}
interface Collection<T> {
readonly elements: T[];
readonly onDidChange: Event<T[]>;
}
class EventCollection<T> implements Collection<T> {
private disposables = new DisposableStore();
get elements(): T[] {
return this._elements;
}
constructor(readonly onDidChange: Event<T[]>, private _elements: T[] = []) {
onDidChange(e => this._elements = e, null, this.disposables);
}
dispose() {
this.disposables.dispose();
}
}
class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>> {
@@ -202,13 +239,20 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
readonly templateId: string;
private renderedElements = new Map<T, ITreeNode<T, TFilterData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, IRenderData<TTemplateData>>();
private indent: number = TreeRenderer.DefaultIndent;
private _renderIndentGuides: RenderIndentGuides = RenderIndentGuides.None;
private renderedIndentGuides = new SetMap<ITreeNode<T, TFilterData>, HTMLDivElement>();
private activeParentNodes = new Set<ITreeNode<T, TFilterData>>();
private indentGuidesDisposable: IDisposable = Disposable.None;
private disposables: IDisposable[] = [];
constructor(
private renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>>,
private activeNodes: Collection<ITreeNode<T, TFilterData>>,
options: ITreeRendererOptions = {}
) {
this.templateId = renderer.templateId;
@@ -226,34 +270,57 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
this.indent = clamp(options.indent, 0, 40);
}
this.renderedNodes.forEach((templateData, node) => {
templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`;
});
if (typeof options.renderIndentGuides !== 'undefined') {
const renderIndentGuides = options.renderIndentGuides;
if (renderIndentGuides !== this._renderIndentGuides) {
this._renderIndentGuides = renderIndentGuides;
if (renderIndentGuides) {
const disposables = new DisposableStore();
this.activeNodes.onDidChange(this._onDidChangeActiveNodes, this, disposables);
this.indentGuidesDisposable = disposables;
this._onDidChangeActiveNodes(this.activeNodes.elements);
} else {
this.indentGuidesDisposable.dispose();
}
}
}
}
renderTemplate(container: HTMLElement): ITreeListTemplateData<TTemplateData> {
const el = append(container, $('.monaco-tl-row'));
const indent = append(el, $('.monaco-tl-indent'));
const twistie = append(el, $('.monaco-tl-twistie'));
const contents = append(el, $('.monaco-tl-contents'));
const templateData = this.renderer.renderTemplate(contents);
return { container, twistie, templateData };
return { container, indent, twistie, indentGuidesDisposable: Disposable.None, templateData };
}
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
if (typeof height === 'number') {
this.renderedNodes.set(node, templateData);
this.renderedNodes.set(node, { templateData, height });
this.renderedElements.set(node.element, node);
}
const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent;
templateData.twistie.style.marginLeft = `${indent}px`;
this.update(node, templateData);
templateData.indent.style.width = `${indent + this.indent - 16}px`;
this.renderTwistie(node, templateData);
if (typeof height === 'number') {
this.renderIndentGuides(node, templateData);
}
this.renderer.renderElement(node, index, templateData.templateData, height);
}
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
templateData.indentGuidesDisposable.dispose();
if (this.renderer.disposeElement) {
this.renderer.disposeElement(node, index, templateData.templateData, height);
}
@@ -279,16 +346,17 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
}
private onDidChangeNodeTwistieState(node: ITreeNode<T, TFilterData>): void {
const templateData = this.renderedNodes.get(node);
const data = this.renderedNodes.get(node);
if (!templateData) {
if (!data) {
return;
}
this.update(node, templateData);
this.renderTwistie(node, data.templateData);
this.renderIndentGuides(node, data.templateData);
}
private update(node: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>) {
private renderTwistie(node: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>) {
if (this.renderer.renderTwistie) {
this.renderer.renderTwistie(node.element, templateData.twistie);
}
@@ -303,9 +371,72 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
}
}
private renderIndentGuides(target: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>): void {
clearNode(templateData.indent);
templateData.indentGuidesDisposable.dispose();
if (this._renderIndentGuides === RenderIndentGuides.None) {
return;
}
const disposableStore = new DisposableStore();
let node = target;
while (node.parent && node.parent.parent) {
const parent = node.parent;
const guide = $<HTMLDivElement>('.indent-guide', { style: `width: ${this.indent}px` });
if (this.activeParentNodes.has(parent)) {
addClass(guide, 'active');
}
if (templateData.indent.childElementCount === 0) {
templateData.indent.appendChild(guide);
} else {
templateData.indent.insertBefore(guide, templateData.indent.firstElementChild);
}
this.renderedIndentGuides.add(parent, guide);
disposableStore.add(toDisposable(() => this.renderedIndentGuides.delete(parent, guide)));
node = parent;
}
templateData.indentGuidesDisposable = disposableStore;
}
private _onDidChangeActiveNodes(nodes: ITreeNode<T, TFilterData>[]): void {
if (this._renderIndentGuides === RenderIndentGuides.None) {
return;
}
const set = new Set<ITreeNode<T, TFilterData>>();
nodes.forEach(node => {
if (node.parent) {
set.add(node.parent);
}
});
this.activeParentNodes.forEach(node => {
if (!set.has(node)) {
this.renderedIndentGuides.forEach(node, line => removeClass(line, 'active'));
}
});
set.forEach(node => {
if (!this.activeParentNodes.has(node)) {
this.renderedIndentGuides.forEach(node, line => addClass(line, 'active'));
}
});
this.activeParentNodes = set;
}
dispose(): void {
this.renderedNodes.clear();
this.renderedElements.clear();
this.indentGuidesDisposable.dispose();
this.disposables = dispose(this.disposables);
}
}
@@ -722,9 +853,18 @@ function asTreeEvent<T>(event: IListEvent<ITreeNode<T, any>>): ITreeEvent<T> {
}
function asTreeMouseEvent<T>(event: IListMouseEvent<ITreeNode<T, any>>): ITreeMouseEvent<T> {
let target: TreeMouseEventTarget = TreeMouseEventTarget.Unknown;
if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-twistie', 'monaco-tl-row')) {
target = TreeMouseEventTarget.Twistie;
} else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-contents', 'monaco-tl-row')) {
target = TreeMouseEventTarget.Element;
}
return {
browserEvent: event.browserEvent,
element: event.element ? event.element.element : null
element: event.element ? event.element.element : null,
target
};
}
@@ -811,6 +951,10 @@ class Trait<T> {
return [...this.elements];
}
getNodes(): readonly ITreeNode<T, any>[] {
return this.nodes;
}
has(node: ITreeNode<T, any>): boolean {
return this.nodeSet.has(node);
}
@@ -999,6 +1143,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
private eventBufferer = new EventBufferer();
private typeFilterController?: TypeFilterController<T, TFilterData>;
private focusNavigationFilter: ((node: ITreeNode<T, TFilterData>) => boolean) | undefined;
private styleElement: HTMLStyleElement;
protected disposables: IDisposable[] = [];
get onDidScroll(): Event<ScrollEvent> { return this.view.onDidScroll; }
@@ -1027,7 +1172,6 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
get filterOnType(): boolean { return !!this._options.filterOnType; }
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
// Options TODO@joao expose options only, not Optional<>
get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; }
@@ -1045,7 +1189,11 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
const onDidChangeCollapseStateRelay = new Relay<ICollapseStateChangeEvent<T, TFilterData>>();
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, _options));
const onDidChangeActiveNodes = new Relay<ITreeNode<T, TFilterData>[]>();
const activeNodes = new EventCollection(onDidChangeActiveNodes.event);
this.disposables.push(activeNodes);
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, activeNodes, _options));
this.disposables.push(...this.renderers);
let filter: TypeFilter<T> | undefined;
@@ -1068,6 +1216,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.selection.onDidModelSplice(e);
}, null, this.disposables);
onDidChangeActiveNodes.input = Event.map(Event.any<any>(this.focus.onDidChange, this.selection.onDidChange, this.model.onDidSplice), () => [...this.focus.getNodes(), ...this.selection.getNodes()]);
if (_options.keyboardSupport !== false) {
const onKeyDown = Event.chain(this.view.onKeyDown)
.filter(e => !isInputElement(e.target as HTMLElement))
@@ -1083,6 +1233,9 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node);
this.disposables.push(this.typeFilterController!);
}
this.styleElement = createStyleSheet(this.view.getHTMLElement());
toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always);
}
updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void {
@@ -1102,6 +1255,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
this._onDidUpdateOptions.fire(this._options);
toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always);
}
get options(): IAbstractTreeOptions<T, TFilterData> {
@@ -1183,6 +1338,19 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
style(styles: IListStyles): void {
const suffix = `.${this.view.domId}`;
const content: string[] = [];
if (styles.treeIndentGuidesStroke) {
content.push(`.monaco-list${suffix}:hover .monaco-tl-indent > .indent-guide, .monaco-list${suffix}.always .monaco-tl-indent > .indent-guide { border-color: ${styles.treeIndentGuidesStroke.transparent(0.4)}; }`);
content.push(`.monaco-list${suffix} .monaco-tl-indent > .indent-guide.active { border-color: ${styles.treeIndentGuidesStroke}; }`);
}
const newStyles = content.join('\n');
if (newStyles !== this.styleElement.innerHTML) {
this.styleElement.innerHTML = newStyles;
}
this.view.style(styles);
}