loadedScriptsView.ts 23.0 KB
Newer Older
I
isidor 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
8
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
9
import { normalize, isAbsolute, posix } from 'vs/base/common/path';
10
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
I
isidor 已提交
11 12 13 14
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
I
isidor 已提交
15
import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
16
import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/contrib/debug/common/debug';
17
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
18 19 20 21
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { tildify } from 'vs/base/common/labels';
import { isWindows } from 'vs/base/common/platform';
22
import { URI } from 'vs/base/common/uri';
23
import { ltrim } from 'vs/base/common/strings';
I
isidor 已提交
24
import { RunOnceScheduler } from 'vs/base/common/async';
25
import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel } from 'vs/workbench/browser/labels';
26
import { FileKind } from 'vs/platform/files/common/files';
27
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
28
import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, ITreeElement } from 'vs/base/browser/ui/tree/tree';
J
João Moreno 已提交
29
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
30
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
31
import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService';
32
import { dispose } from 'vs/base/common/lifecycle';
I
isidor 已提交
33
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
34
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
35
import { ILabelService } from 'vs/platform/label/common/label';
36 37
import type { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import type { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
38
import { IViewDescriptorService } from 'vs/workbench/common/views';
39 40
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
41
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
42
import { IPathService } from 'vs/workbench/services/path/common/pathService';
43

44
const NEW_STYLE_COMPRESS = true;
45

46 47 48
// RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt
const URI_SCHEMA_PATTERN = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/;

49 50
type LoadedScriptsItem = BaseTreeItem;

51 52
class BaseTreeItem {

53
	private _showedMoreThanOne: boolean;
I
isidor 已提交
54
	private _children = new Map<string, BaseTreeItem>();
I
isidor 已提交
55
	private _source: Source | undefined;
56

57
	constructor(private _parent: BaseTreeItem | undefined, private _label: string, public readonly isIncompressible = false) {
58
		this._showedMoreThanOne = false;
59 60
	}

61
	isLeaf(): boolean {
I
isidor 已提交
62
		return this._children.size === 0;
63 64
	}

A
Andre Weinand 已提交
65
	getSession(): IDebugSession | undefined {
66 67 68 69 70 71
		if (this._parent) {
			return this._parent.getSession();
		}
		return undefined;
	}

72
	setSource(session: IDebugSession, source: Source): void {
73
		this._source = source;
I
isidor 已提交
74
		this._children.clear();
75 76
		if (source.raw && source.raw.sources) {
			for (const src of source.raw.sources) {
A
Andre Weinand 已提交
77 78
				if (src.name && src.path) {
					const s = new BaseTreeItem(this, src.name);
I
isidor 已提交
79
					this._children.set(src.path, s);
A
Andre Weinand 已提交
80 81 82
					const ss = session.getSource(src);
					s.setSource(session, ss);
				}
83 84
			}
		}
85 86
	}

87
	createIfNeeded<T extends BaseTreeItem>(key: string, factory: (parent: BaseTreeItem, label: string) => T): T {
I
isidor 已提交
88
		let child = <T>this._children.get(key);
89 90
		if (!child) {
			child = factory(this, key);
I
isidor 已提交
91
			this._children.set(key, child);
92
		}
93
		return child;
94 95
	}

I
isidor 已提交
96 97
	getChild(key: string): BaseTreeItem | undefined {
		return this._children.get(key);
98 99
	}

100
	remove(key: string): void {
I
isidor 已提交
101
		this._children.delete(key);
102 103
	}

104 105 106
	removeFromParent(): void {
		if (this._parent) {
			this._parent.remove(this._label);
I
isidor 已提交
107
			if (this._parent._children.size === 0) {
108 109 110 111 112
				this._parent.removeFromParent();
			}
		}
	}

113
	getTemplateId(): string {
114
		return 'id';
115 116
	}

117 118 119
	// a dynamic ID based on the parent chain; required for reparenting (see #55448)
	getId(): string {
		const parent = this.getParent();
120 121 122 123 124
		return parent ? `${parent.getId()}/${this.getInternalId()}` : this.getInternalId();
	}

	getInternalId(): string {
		return this._label;
125 126 127
	}

	// skips intermediate single-child nodes
A
Andre Weinand 已提交
128
	getParent(): BaseTreeItem | undefined {
129
		if (this._parent) {
A
Andre Weinand 已提交
130
			if (this._parent.isSkipped()) {
131 132 133 134 135 136 137
				return this._parent.getParent();
			}
			return this._parent;
		}
		return undefined;
	}

A
Andre Weinand 已提交
138 139 140 141 142 143 144 145 146 147
	isSkipped(): boolean {
		if (this._parent) {
			if (this._parent.oneChild()) {
				return true;	// skipped if I'm the only child of my parents
			}
			return false;
		}
		return true;	// roots are never skipped
	}

148 149 150 151 152 153
	// skips intermediate single-child nodes
	hasChildren(): boolean {
		const child = this.oneChild();
		if (child) {
			return child.hasChildren();
		}
I
isidor 已提交
154
		return this._children.size > 0;
155 156 157
	}

	// skips intermediate single-child nodes
158
	getChildren(): BaseTreeItem[] {
159 160 161 162
		const child = this.oneChild();
		if (child) {
			return child.getChildren();
		}
163
		const array: BaseTreeItem[] = [];
I
isidor 已提交
164 165 166
		for (let child of this._children.values()) {
			array.push(child);
		}
167
		return array.sort((a, b) => this.compare(a, b));
168 169
	}

170
	// skips intermediate single-child nodes
171
	getLabel(separateRootFolder = true): string {
172 173
		const child = this.oneChild();
		if (child) {
B
Benjamin Pasero 已提交
174
			const sep = (this instanceof RootFolderTreeItem && separateRootFolder) ? '' : posix.sep;
175
			return `${this._label}${sep}${child.getLabel()}`;
176
		}
177
		return this._label;
178 179
	}

180
	// skips intermediate single-child nodes
A
Andre Weinand 已提交
181
	getHoverLabel(): string | undefined {
182 183 184
		if (this._source && this._parent && this._parent._source) {
			return this._source.raw.path || this._source.raw.name;
		}
185
		let label = this.getLabel(false);
A
Andre Weinand 已提交
186 187 188 189 190
		const parent = this.getParent();
		if (parent) {
			const hover = parent.getHoverLabel();
			if (hover) {
				return `${hover}/${label}`;
191 192 193 194 195 196
			}
		}
		return label;
	}

	// skips intermediate single-child nodes
I
isidor 已提交
197
	getSource(): Source | undefined {
198 199 200 201 202 203 204 205 206 207 208 209 210 211
		const child = this.oneChild();
		if (child) {
			return child.getSource();
		}
		return this._source;
	}

	protected compare(a: BaseTreeItem, b: BaseTreeItem): number {
		if (a._label && b._label) {
			return a._label.localeCompare(b._label);
		}
		return 0;
	}

A
Andre Weinand 已提交
212
	private oneChild(): BaseTreeItem | undefined {
213
		if (!this._source && !this._showedMoreThanOne && this.skipOneChild()) {
I
isidor 已提交
214 215
			if (this._children.size === 1) {
				return this._children.values().next().value;
216
			}
217
			// if a node had more than one child once, it will never be skipped again
I
isidor 已提交
218
			if (this._children.size > 1) {
219 220
				this._showedMoreThanOne = true;
			}
221 222 223
		}
		return undefined;
	}
224 225 226 227 228 229 230 231 232

	private skipOneChild(): boolean {
		if (NEW_STYLE_COMPRESS) {
			// if the root node has only one Session, don't show the session
			return this instanceof RootTreeItem;
		} else {
			return !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem);
		}
	}
233 234 235 236 237
}

class RootFolderTreeItem extends BaseTreeItem {

	constructor(parent: BaseTreeItem, public folder: IWorkspaceFolder) {
238
		super(parent, folder.name, true);
239 240 241 242 243
	}
}

class RootTreeItem extends BaseTreeItem {

244
	constructor(private _pathService: IPathService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) {
245 246 247
		super(undefined, 'Root');
	}

248
	add(session: IDebugSession): SessionTreeItem {
249
		return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this._labelService, this, session, this._pathService, this._contextService));
250
	}
251 252 253 254

	find(session: IDebugSession): SessionTreeItem {
		return <SessionTreeItem>this.getChild(session.getId());
	}
255 256 257 258
}

class SessionTreeItem extends BaseTreeItem {

259
	private static readonly URL_REGEXP = /^(https?:\/\/[^/]+)(\/.*)$/;
260

261
	private _session: IDebugSession;
I
isidor 已提交
262
	private _map = new Map<string, BaseTreeItem>();
263
	private _labelService: ILabelService;
264

265
	constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _pathService: IPathService, private rootProvider: IWorkspaceContextService) {
266
		super(parent, session.getLabel(), true);
267
		this._labelService = labelService;
268
		this._session = session;
269 270
	}

271 272 273 274
	getInternalId(): string {
		return this._session.getId();
	}

275 276
	getSession(): IDebugSession {
		return this._session;
277 278
	}

A
Andre Weinand 已提交
279
	getHoverLabel(): string | undefined {
280 281 282
		return undefined;
	}

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
	hasChildren(): boolean {
		return true;
	}

	protected compare(a: BaseTreeItem, b: BaseTreeItem): number {
		const acat = this.category(a);
		const bcat = this.category(b);
		if (acat !== bcat) {
			return acat - bcat;
		}
		return super.compare(a, b);
	}

	private category(item: BaseTreeItem): number {

		// workspace scripts come at the beginning in "folder" order
		if (item instanceof RootFolderTreeItem) {
			return item.folder.index;
		}

		// <...> come at the very end
		const l = item.getLabel();
		if (l && /^<.+>$/.test(l)) {
			return 1000;
		}

		// everything else in between
		return 999;
	}

313
	async addPath(source: Source): Promise<void> {
314

A
Andre Weinand 已提交
315
		let folder: IWorkspaceFolder | null;
316 317 318
		let url: string;

		let path = source.raw.path;
A
Andre Weinand 已提交
319 320 321
		if (!path) {
			return;
		}
322

323 324 325 326
		if (this._labelService && URI_SCHEMA_PATTERN.test(path)) {
			path = this._labelService.getUriLabel(URI.parse(path));
		}

327 328 329 330 331 332 333 334 335 336 337 338
		const match = SessionTreeItem.URL_REGEXP.exec(path);
		if (match && match.length === 3) {
			url = match[1];
			path = decodeURI(match[2]);
		} else {
			if (isAbsolute(path)) {
				const resource = URI.file(path);

				// return early if we can resolve a relative path label from the root folder
				folder = this.rootProvider ? this.rootProvider.getWorkspaceFolder(resource) : null;
				if (folder) {
					// strip off the root folder path
B
Benjamin Pasero 已提交
339
					path = normalize(ltrim(resource.path.substr(folder.uri.path.length), posix.sep));
340 341
					const hasMultipleRoots = this.rootProvider.getWorkspace().folders.length > 1;
					if (hasMultipleRoots) {
B
Benjamin Pasero 已提交
342
						path = posix.sep + path;
343 344
					} else {
						// don't show root folder
A
Andre Weinand 已提交
345
						folder = null;
346 347 348
					}
				} else {
					// on unix try to tildify absolute paths
349
					path = normalize(path);
350
					if (!isWindows) {
351
						path = tildify(path, (await this._pathService.userHome()).fsPath);
352 353 354 355 356
					}
				}
			}
		}

357
		let leaf: BaseTreeItem = this;
358 359
		path.split(/[\/\\]/).forEach((segment, i) => {
			if (i === 0 && folder) {
A
Andre Weinand 已提交
360 361
				const f = folder;
				leaf = leaf.createIfNeeded(folder.name, parent => new RootFolderTreeItem(parent, f));
362
			} else if (i === 0 && url) {
363
				leaf = leaf.createIfNeeded(url, parent => new BaseTreeItem(parent, url));
364
			} else {
365
				leaf = leaf.createIfNeeded(segment, parent => new BaseTreeItem(parent, segment));
366 367 368
			}
		});

369
		leaf.setSource(this._session, source);
A
Andre Weinand 已提交
370 371 372
		if (source.raw.path) {
			this._map.set(source.raw.path, leaf);
		}
373 374 375
	}

	removePath(source: Source): boolean {
A
Andre Weinand 已提交
376 377 378 379 380 381
		if (source.raw.path) {
			const leaf = this._map.get(source.raw.path);
			if (leaf) {
				leaf.removeFromParent();
				return true;
			}
382 383
		}
		return false;
384 385
	}
}
I
isidor 已提交
386

387 388 389 390
interface IViewState {
	readonly expanded: Set<string>;
}

J
Joao Moreno 已提交
391 392 393
/**
 * This maps a model item into a view model item.
 */
394
function asTreeElement(item: BaseTreeItem, viewState?: IViewState): ITreeElement<LoadedScriptsItem> {
J
Joao Moreno 已提交
395
	const children = item.getChildren();
396
	const collapsed = viewState ? !viewState.expanded.has(item.getId()) : !(item instanceof SessionTreeItem);
J
Joao Moreno 已提交
397 398 399

	return {
		element: item,
400
		collapsed,
J
Joao Moreno 已提交
401
		collapsible: item.hasChildren(),
402
		children: children.map(i => asTreeElement(i, viewState))
J
Joao Moreno 已提交
403 404 405
	};
}

I
isidor 已提交
406
export class LoadedScriptsView extends ViewPane {
I
isidor 已提交
407

I
isidor 已提交
408
	private treeContainer!: HTMLElement;
409
	private loadedScriptsItemType: IContextKey<string>;
410
	private tree!: WorkbenchCompressibleObjectTree<LoadedScriptsItem, FuzzyScore>;
I
isidor 已提交
411 412 413 414
	private treeLabels!: ResourceLabels;
	private changeScheduler!: RunOnceScheduler;
	private treeNeedsRefreshOnVisible = false;
	private filter!: LoadedScriptsFilter;
I
isidor 已提交
415 416 417 418 419

	constructor(
		options: IViewletViewOptions,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IKeybindingService keybindingService: IKeybindingService,
420
		@IInstantiationService instantiationService: IInstantiationService,
421
		@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
I
isidor 已提交
422
		@IConfigurationService configurationService: IConfigurationService,
423
		@IEditorService private readonly editorService: IEditorService,
424
		@IContextKeyService readonly contextKeyService: IContextKeyService,
425 426
		@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
		@IDebugService private readonly debugService: IDebugService,
427
		@ILabelService private readonly labelService: ILabelService,
428
		@IPathService private readonly pathService: IPathService,
429 430
		@IOpenerService openerService: IOpenerService,
		@IThemeService themeService: IThemeService,
431
		@ITelemetryService telemetryService: ITelemetryService,
I
isidor 已提交
432
	) {
433
		super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
434
		this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService);
I
isidor 已提交
435 436
	}

437
	renderBody(container: HTMLElement): void {
J
Joao Moreno 已提交
438 439
		super.renderBody(container);

I
isidor 已提交
440
		dom.addClass(this.element, 'debug-pane');
I
isidor 已提交
441
		dom.addClass(container, 'debug-loaded-scripts');
442
		dom.addClass(container, 'show-file-icons');
443

I
isidor 已提交
444 445
		this.treeContainer = renderViewTree(container);

446 447
		this.filter = new LoadedScriptsFilter();

448
		const root = new RootTreeItem(this.pathService, this.contextService, this.labelService);
449

450
		this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility });
M
Matt Bierner 已提交
451
		this._register(this.treeLabels);
452

453
		this.tree = <WorkbenchCompressibleObjectTree<LoadedScriptsItem, FuzzyScore>>this.instantiationService.createInstance(WorkbenchCompressibleObjectTree,
454 455 456
			'LoadedScriptsView',
			this.treeContainer,
			new LoadedScriptsDelegate(),
B
Benjamin Pasero 已提交
457
			[new LoadedScriptsRenderer(this.treeLabels)],
458
			{
459
				compressionEnabled: NEW_STYLE_COMPRESS,
J
Joao Moreno 已提交
460
				collapseByDefault: true,
461
				hideTwistiesOfChildlessElements: true,
462
				identityProvider: {
B
Benjamin Pasero 已提交
463
					getId: (element: LoadedScriptsItem) => element.getId()
464
				},
J
Joao Moreno 已提交
465
				keyboardNavigationLabelProvider: {
466 467 468 469 470 471
					getKeyboardNavigationLabel: (element: LoadedScriptsItem) => {
						return element.getLabel();
					},
					getCompressedNodeKeyboardNavigationLabel: (elements: LoadedScriptsItem[]) => {
						return elements.map(e => e.getLabel()).join('/');
					}
472
				},
473
				filter: this.filter,
474
				accessibilityProvider: new LoadedSciptsAccessibilityProvider(),
475
				overrideStyles: {
S
SteVen Batten 已提交
476
					listBackground: this.getBackgroundColor()
477
				}
478
			}
479
		);
480

481
		const updateView = (viewState?: IViewState) => this.tree.setChildren(null, asTreeElement(root, viewState).children);
J
Joao Moreno 已提交
482 483

		updateView();
J
Joao Moreno 已提交
484

485 486 487
		this.changeScheduler = new RunOnceScheduler(() => {
			this.treeNeedsRefreshOnVisible = false;
			if (this.tree) {
J
Joao Moreno 已提交
488
				updateView();
489 490
			}
		}, 300);
M
Matt Bierner 已提交
491
		this._register(this.changeScheduler);
492

493
		this._register(this.tree.onDidOpen(e => {
494 495
			if (e.element instanceof BaseTreeItem) {
				const source = e.element.getSource();
496 497
				if (source && source.available) {
					const nullRange = { startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 };
498
					source.openInEditor(this.editorService, nullRange, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned);
499 500 501 502
				}
			}
		}));

M
Matt Bierner 已提交
503
		this._register(this.tree.onDidChangeFocus(() => {
504 505 506 507 508 509 510 511
			const focus = this.tree.getFocus();
			if (focus instanceof SessionTreeItem) {
				this.loadedScriptsItemType.set('session');
			} else {
				this.loadedScriptsItemType.reset();
			}
		}));

512 513 514 515 516 517 518 519
		const scheduleRefreshOnVisible = () => {
			if (this.isBodyVisible()) {
				this.changeScheduler.schedule();
			} else {
				this.treeNeedsRefreshOnVisible = true;
			}
		};

520
		const addSourcePathsToSession = async (session: IDebugSession) => {
521
			const sessionNode = root.add(session);
522 523 524 525 526
			const paths = await session.getLoadedSources();
			for (const path of paths) {
				await sessionNode.addPath(path);
			}
			scheduleRefreshOnVisible();
527 528
		};

529
		const registerSessionListeners = (session: IDebugSession) => {
530
			this._register(session.onDidChangeName(async () => {
531 532
				// Re-add session, this will trigger proper sorting and id recalculation.
				root.remove(session.getId());
533
				await addSourcePathsToSession(session);
534
			}));
535
			this._register(session.onDidLoadedSource(async event => {
536 537 538
				let sessionRoot: SessionTreeItem;
				switch (event.reason) {
					case 'new':
539
					case 'changed':
540
						sessionRoot = root.add(session);
541
						await sessionRoot.addPath(event.source);
542
						scheduleRefreshOnVisible();
543 544 545
						if (event.reason === 'changed') {
							DebugContentProvider.refreshDebugContent(event.source.uri);
						}
546
						break;
547 548 549
					case 'removed':
						sessionRoot = root.find(session);
						if (sessionRoot && sessionRoot.removePath(event.source)) {
550
							scheduleRefreshOnVisible();
551 552
						}
						break;
553 554 555 556
					default:
						this.filter.setFilter(event.source.name);
						this.tree.refilter();
						break;
557
				}
I
isidor 已提交
558
			}));
559 560
		};

561 562
		this._register(this.debugService.onDidNewSession(registerSessionListeners));
		this.debugService.getModel().getSessions().forEach(registerSessionListeners);
563

M
Matt Bierner 已提交
564
		this._register(this.debugService.onDidEndSession(session => {
565
			root.remove(session.getId());
566
			this.changeScheduler.schedule();
567
		}));
568 569

		this.changeScheduler.schedule(0);
570

M
Matt Bierner 已提交
571
		this._register(this.onDidChangeBodyVisibility(visible => {
572 573 574 575
			if (visible && this.treeNeedsRefreshOnVisible) {
				this.changeScheduler.schedule();
			}
		}));
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604

		// feature: expand all nodes when filtering (not when finding)
		let viewState: IViewState | undefined;
		this._register(this.tree.onDidChangeTypeFilterPattern(pattern => {
			if (!this.tree.options.filterOnType) {
				return;
			}

			if (!viewState && pattern) {
				const expanded = new Set<string>();
				const visit = (node: ITreeNode<BaseTreeItem | null, FuzzyScore>) => {
					if (node.element && !node.collapsed) {
						expanded.add(node.element.getId());
					}

					for (const child of node.children) {
						visit(child);
					}
				};

				visit(this.tree.getNode());
				viewState = { expanded };
				this.tree.expandAll();
			} else if (!pattern && viewState) {
				this.tree.setFocus([]);
				updateView(viewState);
				viewState = undefined;
			}
		}));
605 606 607

		// populate tree model with source paths from all debug sessions
		this.debugService.getModel().getSessions().forEach(session => addSourcePathsToSession(session));
I
isidor 已提交
608 609
	}

610
	layoutBody(height: number, width: number): void {
J
João Moreno 已提交
611
		super.layoutBody(height, width);
612
		this.tree.layout(height, width);
613 614
	}

I
isidor 已提交
615
	dispose(): void {
616 617
		dispose(this.tree);
		dispose(this.treeLabels);
618
		super.dispose();
I
isidor 已提交
619
	}
I
isidor 已提交
620 621
}

622
class LoadedScriptsDelegate implements IListVirtualDelegate<LoadedScriptsItem> {
I
isidor 已提交
623

624 625
	getHeight(element: LoadedScriptsItem): number {
		return 22;
I
isidor 已提交
626 627
	}

628
	getTemplateId(element: LoadedScriptsItem): string {
A
Andre Weinand 已提交
629
		return LoadedScriptsRenderer.ID;
I
isidor 已提交
630
	}
631
}
I
isidor 已提交
632

633
interface ILoadedScriptsItemTemplateData {
B
Benjamin Pasero 已提交
634
	label: IResourceLabel;
635 636
}

637
class LoadedScriptsRenderer implements ICompressibleTreeRenderer<BaseTreeItem, FuzzyScore, ILoadedScriptsItemTemplateData> {
638 639

	static readonly ID = 'lsrenderer';
I
isidor 已提交
640

641
	constructor(
642
		private labels: ResourceLabels
643 644 645
	) {
	}

646 647
	get templateId(): string {
		return LoadedScriptsRenderer.ID;
I
isidor 已提交
648 649
	}

650
	renderTemplate(container: HTMLElement): ILoadedScriptsItemTemplateData {
651 652
		const label = this.labels.create(container, { supportHighlights: true });
		return { label };
653
	}
654

I
isidor 已提交
655
	renderElement(node: ITreeNode<BaseTreeItem, FuzzyScore>, index: number, data: ILoadedScriptsItemTemplateData): void {
656 657

		const element = node.element;
658 659 660 661 662 663 664 665 666 667 668 669 670 671
		const label = element.getLabel();

		this.render(element, label, data, node.filterData);
	}

	renderCompressedElements(node: ITreeNode<ICompressedTreeNode<BaseTreeItem>, FuzzyScore>, index: number, data: ILoadedScriptsItemTemplateData, height: number | undefined): void {

		const element = node.element.elements[node.element.elements.length - 1];
		const labels = node.element.elements.map(e => e.getLabel());

		this.render(element, labels, data, node.filterData);
	}

	private render(element: BaseTreeItem, labels: string | string[], data: ILoadedScriptsItemTemplateData, filterData: FuzzyScore | undefined) {
672

B
Benjamin Pasero 已提交
673
		const label: IResourceLabelProps = {
674
			name: labels
675 676 677 678
		};
		const options: IResourceLabelOptions = {
			title: element.getHoverLabel()
		};
679

680
		if (element instanceof RootFolderTreeItem) {
I
isidor 已提交
681

682
			options.fileKind = FileKind.ROOT_FOLDER;
I
isidor 已提交
683

684
		} else if (element instanceof SessionTreeItem) {
685

686 687 688 689 690 691 692 693 694 695 696 697 698
			options.title = nls.localize('loadedScriptsSession', "Debug Session");
			options.hideIcon = true;

		} else if (element instanceof BaseTreeItem) {

			const src = element.getSource();
			if (src && src.uri) {
				label.resource = src.uri;
				options.fileKind = FileKind.FILE;
			} else {
				options.fileKind = FileKind.FOLDER;
			}
		}
699
		options.matches = createMatches(filterData);
700

B
Benjamin Pasero 已提交
701
		data.label.setResource(label, options);
702 703
	}

704
	disposeTemplate(templateData: ILoadedScriptsItemTemplateData): void {
J
Joao Moreno 已提交
705
		templateData.label.dispose();
I
isidor 已提交
706 707 708
	}
}

J
João Moreno 已提交
709
class LoadedSciptsAccessibilityProvider implements IListAccessibilityProvider<LoadedScriptsItem> {
I
isidor 已提交
710

711 712 713 714
	getWidgetAriaLabel(): string {
		return nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'loadedScriptsAriaLabel' }, "Debug Loaded Scripts");
	}

715
	getAriaLabel(element: LoadedScriptsItem): string {
716 717 718 719 720 721 722 723 724

		if (element instanceof RootFolderTreeItem) {
			return nls.localize('loadedScriptsRootFolderAriaLabel', "Workspace folder {0}, loaded script, debug", element.getLabel());
		}

		if (element instanceof SessionTreeItem) {
			return nls.localize('loadedScriptsSessionAriaLabel', "Session {0}, loaded script, debug", element.getLabel());
		}

A
Andre Weinand 已提交
725 726 727 728
		if (element.hasChildren()) {
			return nls.localize('loadedScriptsFolderAriaLabel', "Folder {0}, loaded script, debug", element.getLabel());
		} else {
			return nls.localize('loadedScriptsSourceAriaLabel', "{0}, loaded script, debug", element.getLabel());
729
		}
I
isidor 已提交
730 731
	}
}
732

I
isidor 已提交
733
class LoadedScriptsFilter implements ITreeFilter<BaseTreeItem, FuzzyScore> {
734

I
isidor 已提交
735
	private filterText: string | undefined;
736 737 738 739 740

	setFilter(filterText: string) {
		this.filterText = filterText;
	}

I
isidor 已提交
741
	filter(element: BaseTreeItem, parentVisibility: TreeVisibility): TreeFilterResult<FuzzyScore> {
742 743 744 745 746 747 748 749 750 751 752 753 754 755

		if (!this.filterText) {
			return TreeVisibility.Visible;
		}

		if (element.isLeaf()) {
			const name = element.getLabel();
			if (name.indexOf(this.filterText) >= 0) {
				return TreeVisibility.Visible;
			}
			return TreeVisibility.Hidden;
		}
		return TreeVisibility.Recurse;
	}
I
isidor 已提交
756
}