loadedScriptsView.ts 19.7 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 { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
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';
15 16 17
import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
import { IDebugSession, IDebugService, IDebugModel, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/contrib/debug/common/debug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
18 19 20 21 22
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { tildify } from 'vs/base/common/labels';
import { isWindows } from 'vs/base/common/platform';
23
import { URI } from 'vs/base/common/uri';
24
import { ltrim } from 'vs/base/common/strings';
I
isidor 已提交
25
import { RunOnceScheduler } from 'vs/base/common/async';
26
import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel, IResourceLabelsContainer } from 'vs/workbench/browser/labels';
27
import { FileKind } from 'vs/platform/files/common/files';
28
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
J
Joao Moreno 已提交
29
import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
30 31
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
A
Andre Weinand 已提交
32
import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
I
isidor 已提交
33
import { IThemeService } from 'vs/platform/theme/common/themeService';
34
import { DebugContentProvider } from 'vs/workbench/contrib/debug/browser/debugContentProvider';
35
import { dispose } from 'vs/base/common/lifecycle';
I
isidor 已提交
36
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
37 38 39

const SMART = true;

40 41
type LoadedScriptsItem = BaseTreeItem;

42 43
class BaseTreeItem {

44
	private _showedMoreThanOne: boolean;
45 46 47 48 49
	private _children: { [key: string]: BaseTreeItem; };
	private _source: Source;

	constructor(private _parent: BaseTreeItem, private _label: string) {
		this._children = {};
50
		this._showedMoreThanOne = false;
51 52
	}

53 54 55 56
	isLeaf(): boolean {
		return Object.keys(this._children).length === 0;
	}

57 58 59 60 61 62 63
	getSession(): IDebugSession {
		if (this._parent) {
			return this._parent.getSession();
		}
		return undefined;
	}

64
	setSource(session: IDebugSession, source: Source): void {
65
		this._source = source;
66
		this._children = {};
67 68 69 70 71 72 73 74
		if (source.raw && source.raw.sources) {
			for (const src of source.raw.sources) {
				const s = new BaseTreeItem(this, src.name);
				this._children[src.path] = s;
				const ss = session.getSource(src);
				s.setSource(session, ss);
			}
		}
75 76
	}

77 78 79 80 81
	createIfNeeded<T extends BaseTreeItem>(key: string, factory: (parent: BaseTreeItem, label: string) => T): T {
		let child = <T>this._children[key];
		if (!child) {
			child = factory(this, key);
			this._children[key] = child;
82
		}
83
		return child;
84 85
	}

86 87 88 89
	getChild(key: string): BaseTreeItem {
		return this._children[key];
	}

90 91
	remove(key: string): void {
		delete this._children[key];
92 93
	}

94 95 96 97 98 99 100 101 102
	removeFromParent(): void {
		if (this._parent) {
			this._parent.remove(this._label);
			if (Object.keys(this._parent._children).length === 0) {
				this._parent.removeFromParent();
			}
		}
	}

103
	getTemplateId(): string {
104
		return 'id';
105 106
	}

107 108 109 110 111 112 113
	// a dynamic ID based on the parent chain; required for reparenting (see #55448)
	getId(): string {
		const parent = this.getParent();
		return parent ? `${parent.getId()}/${this._label}` : this._label;
	}

	// skips intermediate single-child nodes
114 115
	getParent(): BaseTreeItem {
		if (this._parent) {
A
Andre Weinand 已提交
116
			if (this._parent.isSkipped()) {
117 118 119 120 121 122 123
				return this._parent.getParent();
			}
			return this._parent;
		}
		return undefined;
	}

A
Andre Weinand 已提交
124 125 126 127 128 129 130 131 132 133
	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
	}

134 135 136 137 138 139 140 141 142 143
	// skips intermediate single-child nodes
	hasChildren(): boolean {
		const child = this.oneChild();
		if (child) {
			return child.hasChildren();
		}
		return Object.keys(this._children).length > 0;
	}

	// skips intermediate single-child nodes
144
	getChildren(): Promise<BaseTreeItem[]> {
145 146 147 148 149
		const child = this.oneChild();
		if (child) {
			return child.getChildren();
		}
		const array = Object.keys(this._children).map(key => this._children[key]);
I
isidor 已提交
150
		return Promise.resolve(array.sort((a, b) => this.compare(a, b)));
151 152
	}

153
	// skips intermediate single-child nodes
154
	getLabel(separateRootFolder = true): string {
155 156
		const child = this.oneChild();
		if (child) {
B
Benjamin Pasero 已提交
157
			const sep = (this instanceof RootFolderTreeItem && separateRootFolder) ? '' : posix.sep;
158
			return `${this._label}${sep}${child.getLabel()}`;
159
		}
160
		return this._label;
161 162
	}

163 164
	// skips intermediate single-child nodes
	getHoverLabel(): string {
165 166 167
		if (this._source && this._parent && this._parent._source) {
			return this._source.raw.path || this._source.raw.name;
		}
168
		let label = this.getLabel(false);
A
Andre Weinand 已提交
169 170 171 172 173
		const parent = this.getParent();
		if (parent) {
			const hover = parent.getHoverLabel();
			if (hover) {
				return `${hover}/${label}`;
174 175 176 177 178 179
			}
		}
		return label;
	}

	// skips intermediate single-child nodes
180
	getSource(): Source {
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
		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;
	}

	private oneChild(): BaseTreeItem {
196
		if (SMART && !this._source && !this._showedMoreThanOne && !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem)) {
197 198 199 200
			const keys = Object.keys(this._children);
			if (keys.length === 1) {
				return this._children[keys[0]];
			}
201 202 203 204
			// if a node had more than one child once, it will never be skipped again
			if (keys.length > 1) {
				this._showedMoreThanOne = true;
			}
205 206 207 208 209 210 211 212 213 214 215 216 217 218
		}
		return undefined;
	}
}

class RootFolderTreeItem extends BaseTreeItem {

	constructor(parent: BaseTreeItem, public folder: IWorkspaceFolder) {
		super(parent, folder.name);
	}
}

class RootTreeItem extends BaseTreeItem {

219
	constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) {
220 221 222 223 224 225
		super(undefined, 'Root');
		this._debugModel.getSessions().forEach(session => {
			this.add(session);
		});
	}

226
	add(session: IDebugSession): SessionTreeItem {
227 228
		return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this, session, this._environmentService, this._contextService));
	}
229 230 231 232

	find(session: IDebugSession): SessionTreeItem {
		return <SessionTreeItem>this.getChild(session.getId());
	}
233 234 235 236 237 238
}

class SessionTreeItem extends BaseTreeItem {

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

239
	private _session: IDebugSession;
240
	private _initialized: boolean;
241
	private _map: Map<string, BaseTreeItem>;
242

243
	constructor(parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) {
I
isidor 已提交
244
		super(parent, session.getLabel());
245 246
		this._initialized = false;
		this._session = session;
247 248 249 250 251
		this._map = new Map();
	}

	getSession(): IDebugSession {
		return this._session;
252 253
	}

254 255 256 257
	getHoverLabel(): string {
		return undefined;
	}

258 259 260 261
	hasChildren(): boolean {
		return true;
	}

262
	getChildren(): Promise<BaseTreeItem[]> {
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 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 313 314 315 316 317 318 319

		if (!this._initialized) {
			this._initialized = true;
			return this._session.getLoadedSources().then(paths => {
				paths.forEach(path => this.addPath(path));
				return super.getChildren();
			});
		}

		return super.getChildren();
	}

	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;
	}

	addPath(source: Source): void {

		let folder: IWorkspaceFolder;
		let url: string;

		let path = source.raw.path;

		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 已提交
320
					path = normalize(ltrim(resource.path.substr(folder.uri.path.length), posix.sep));
321 322
					const hasMultipleRoots = this.rootProvider.getWorkspace().folders.length > 1;
					if (hasMultipleRoots) {
B
Benjamin Pasero 已提交
323
						path = posix.sep + path;
324 325 326 327 328 329
					} else {
						// don't show root folder
						folder = undefined;
					}
				} else {
					// on unix try to tildify absolute paths
330
					path = normalize(path);
331 332 333 334 335 336 337
					if (!isWindows) {
						path = tildify(path, this._environmentService.userHome);
					}
				}
			}
		}

338
		let leaf: BaseTreeItem = this;
339 340
		path.split(/[\/\\]/).forEach((segment, i) => {
			if (i === 0 && folder) {
341
				leaf = leaf.createIfNeeded(folder.name, parent => new RootFolderTreeItem(parent, folder));
342
			} else if (i === 0 && url) {
343
				leaf = leaf.createIfNeeded(url, parent => new BaseTreeItem(parent, url));
344
			} else {
345
				leaf = leaf.createIfNeeded(segment, parent => new BaseTreeItem(parent, segment));
346 347 348
			}
		});

349 350 351 352 353 354 355 356 357 358 359
		leaf.setSource(this._session, source);
		this._map.set(source.raw.path, leaf);
	}

	removePath(source: Source): boolean {
		const leaf = this._map.get(source.raw.path);
		if (leaf) {
			leaf.removeFromParent();
			return true;
		}
		return false;
360 361
	}
}
I
isidor 已提交
362

363
export class LoadedScriptsView extends ViewletPanel {
I
isidor 已提交
364 365

	private treeContainer: HTMLElement;
366
	private loadedScriptsItemType: IContextKey<string>;
I
isidor 已提交
367
	private tree: WorkbenchAsyncDataTree<LoadedScriptsItem, LoadedScriptsItem, FuzzyScore>;
B
Benjamin Pasero 已提交
368
	private treeLabels: ResourceLabels;
369 370
	private changeScheduler: RunOnceScheduler;
	private treeNeedsRefreshOnVisible: boolean;
371
	private filter: LoadedScriptsFilter;
I
isidor 已提交
372 373 374 375 376

	constructor(
		options: IViewletViewOptions,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IKeybindingService keybindingService: IKeybindingService,
377
		@IInstantiationService private readonly instantiationService: IInstantiationService,
I
isidor 已提交
378
		@IConfigurationService configurationService: IConfigurationService,
379 380 381 382 383 384 385
		@IEditorService private readonly editorService: IEditorService,
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
		@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
		@IDebugService private readonly debugService: IDebugService,
		@IListService private readonly listService: IListService,
		@IThemeService private readonly themeService: IThemeService
I
isidor 已提交
386 387
	) {
		super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService);
388
		this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService);
I
isidor 已提交
389 390
	}

391
	renderBody(container: HTMLElement): void {
I
isidor 已提交
392
		dom.addClass(container, 'debug-loaded-scripts');
393
		dom.addClass(container, 'show-file-icons');
394

I
isidor 已提交
395 396
		this.treeContainer = renderViewTree(container);

397 398
		this.filter = new LoadedScriptsFilter();

399 400
		const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService);

401
		this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer);
B
Benjamin Pasero 已提交
402
		this.disposables.push(this.treeLabels);
403

A
Andre Weinand 已提交
404
		this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new LoadedScriptsDelegate(),
B
Benjamin Pasero 已提交
405
			[new LoadedScriptsRenderer(this.treeLabels)],
J
Joao Moreno 已提交
406
			new LoadedScriptsDataSource(),
407
			{
408 409 410
				identityProvider: {
					getId: element => element.getId()
				},
J
Joao Moreno 已提交
411 412
				keyboardNavigationLabelProvider: {
					getKeyboardNavigationLabel: element => element.getLabel()
413
				},
414
				filter: this.filter,
415
				accessibilityProvider: new LoadedSciptsAccessibilityProvider(),
416
				ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'loadedScriptsAriaLabel' }, "Debug Loaded Scripts"),
I
isidor 已提交
417
			},
418
			this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService
419 420
		);

J
Joao Moreno 已提交
421 422
		this.tree.setInput(root);

423 424 425
		this.changeScheduler = new RunOnceScheduler(() => {
			this.treeNeedsRefreshOnVisible = false;
			if (this.tree) {
426
				this.tree.updateChildren();
427 428 429
			}
		}, 300);
		this.disposables.push(this.changeScheduler);
430

I
isidor 已提交
431
		const loadedScriptsNavigator = new TreeResourceNavigator2(this.tree);
432
		this.disposables.push(loadedScriptsNavigator);
J
Joao Moreno 已提交
433
		this.disposables.push(loadedScriptsNavigator.onDidOpenResource(e => {
434 435
			if (e.element instanceof BaseTreeItem) {
				const source = e.element.getSource();
436 437
				if (source && source.available) {
					const nullRange = { startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 };
438
					source.openInEditor(this.editorService, nullRange, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned);
439 440 441 442 443 444 445 446 447 448 449 450 451
				}
			}
		}));

		this.disposables.push(this.tree.onDidChangeFocus(() => {
			const focus = this.tree.getFocus();
			if (focus instanceof SessionTreeItem) {
				this.loadedScriptsItemType.set('session');
			} else {
				this.loadedScriptsItemType.reset();
			}
		}));

452
		const registerLoadedSourceListener = (session: IDebugSession) => {
I
isidor 已提交
453
			this.disposables.push(session.onDidLoadedSource(event => {
454 455 456
				let sessionRoot: SessionTreeItem;
				switch (event.reason) {
					case 'new':
457
					case 'changed':
458 459
						sessionRoot = root.add(session);
						sessionRoot.addPath(event.source);
460
						if (this.isBodyVisible()) {
461 462 463 464
							this.changeScheduler.schedule();
						} else {
							this.treeNeedsRefreshOnVisible = true;
						}
465 466 467
						if (event.reason === 'changed') {
							DebugContentProvider.refreshDebugContent(event.source.uri);
						}
468
						break;
469 470 471
					case 'removed':
						sessionRoot = root.find(session);
						if (sessionRoot && sessionRoot.removePath(event.source)) {
472
							if (this.isBodyVisible()) {
473 474 475 476
								this.changeScheduler.schedule();
							} else {
								this.treeNeedsRefreshOnVisible = true;
							}
477 478
						}
						break;
479 480 481 482
					default:
						this.filter.setFilter(event.source.name);
						this.tree.refilter();
						break;
483
				}
I
isidor 已提交
484
			}));
485 486 487 488
		};

		this.disposables.push(this.debugService.onDidNewSession(registerLoadedSourceListener));
		this.debugService.getModel().getSessions().forEach(registerLoadedSourceListener);
489 490 491

		this.disposables.push(this.debugService.onDidEndSession(session => {
			root.remove(session.getId());
492
			this.changeScheduler.schedule();
493
		}));
494 495

		this.changeScheduler.schedule(0);
496 497 498 499 500 501

		this.disposables.push(this.onDidChangeBodyVisibility(visible => {
			if (visible && this.treeNeedsRefreshOnVisible) {
				this.changeScheduler.schedule();
			}
		}));
I
isidor 已提交
502 503
	}

504 505
	layoutBody(height: number, width: number): void {
		this.tree.layout(height, width);
506 507
	}

I
isidor 已提交
508
	dispose(): void {
509
		this.tree = dispose(this.tree);
B
Benjamin Pasero 已提交
510
		this.treeLabels = dispose(this.treeLabels);
511
		super.dispose();
I
isidor 已提交
512
	}
I
isidor 已提交
513 514
}

515
class LoadedScriptsDelegate implements IListVirtualDelegate<LoadedScriptsItem> {
I
isidor 已提交
516

517 518
	getHeight(element: LoadedScriptsItem): number {
		return 22;
I
isidor 已提交
519 520
	}

521 522 523 524 525
	getTemplateId(element: LoadedScriptsItem): string {
		if (element instanceof BaseTreeItem) {
			return LoadedScriptsRenderer.ID;
		}
		return undefined;
I
isidor 已提交
526
	}
527
}
I
isidor 已提交
528

J
Joao Moreno 已提交
529
class LoadedScriptsDataSource implements IAsyncDataSource<LoadedScriptsItem, LoadedScriptsItem> {
I
isidor 已提交
530

J
Joao Moreno 已提交
531 532
	hasChildren(element: LoadedScriptsItem): boolean {
		return element.hasChildren();
533 534
	}

J
Joao Moreno 已提交
535
	getChildren(element: LoadedScriptsItem): Promise<LoadedScriptsItem[]> {
536
		return element.getChildren();
I
isidor 已提交
537 538 539
	}
}

540
interface ILoadedScriptsItemTemplateData {
B
Benjamin Pasero 已提交
541
	label: IResourceLabel;
542 543
}

I
isidor 已提交
544
class LoadedScriptsRenderer implements ITreeRenderer<BaseTreeItem, FuzzyScore, ILoadedScriptsItemTemplateData> {
545 546

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

548
	constructor(
549
		private labels: ResourceLabels
550 551 552
	) {
	}

553 554
	get templateId(): string {
		return LoadedScriptsRenderer.ID;
I
isidor 已提交
555 556
	}

557
	renderTemplate(container: HTMLElement): ILoadedScriptsItemTemplateData {
558 559
		const label = this.labels.create(container, { supportHighlights: true });
		return { label };
560
	}
561

I
isidor 已提交
562
	renderElement(node: ITreeNode<BaseTreeItem, FuzzyScore>, index: number, data: ILoadedScriptsItemTemplateData): void {
563 564

		const element = node.element;
565

B
Benjamin Pasero 已提交
566
		const label: IResourceLabelProps = {
567 568 569 570 571
			name: element.getLabel()
		};
		const options: IResourceLabelOptions = {
			title: element.getHoverLabel()
		};
572

573
		if (element instanceof RootFolderTreeItem) {
I
isidor 已提交
574

575
			options.fileKind = FileKind.ROOT_FOLDER;
I
isidor 已提交
576

577
		} else if (element instanceof SessionTreeItem) {
578

579 580 581 582 583 584 585 586 587 588 589 590 591
			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;
			}
		}
I
isidor 已提交
592
		options.matches = createMatches(node.filterData);
593

B
Benjamin Pasero 已提交
594
		data.label.setResource(label, options);
595 596
	}

597
	disposeTemplate(templateData: ILoadedScriptsItemTemplateData): void {
J
Joao Moreno 已提交
598
		templateData.label.dispose();
I
isidor 已提交
599 600 601
	}
}

602
class LoadedSciptsAccessibilityProvider implements IAccessibilityProvider<LoadedScriptsItem> {
I
isidor 已提交
603

604
	getAriaLabel(element: LoadedScriptsItem): string {
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620

		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());
		}

		if (element instanceof BaseTreeItem) {
			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());
			}
		}
621

622
		return null;
I
isidor 已提交
623 624
	}
}
625

I
isidor 已提交
626
class LoadedScriptsFilter implements ITreeFilter<BaseTreeItem, FuzzyScore> {
627 628 629 630 631 632 633

	private filterText: string;

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

I
isidor 已提交
634
	filter(element: BaseTreeItem, parentVisibility: TreeVisibility): TreeFilterResult<FuzzyScore> {
635 636 637 638 639 640 641 642 643 644 645 646 647 648

		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 已提交
649
}