@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
129 lines (110 loc) • 4.69 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2018 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { inject, injectable, postConstruct } from 'inversify';
import { Disposable, DisposableCollection } from '../../common/disposable';
import { Event, Emitter } from '../../common/event';
import { Tree, TreeNode } from './tree';
import { TreeDecoration } from './tree-decorator';
import { FuzzySearch } from './fuzzy-search';
import { TopDownTreeIterator } from './tree-iterator';
import { LabelProvider } from '../label-provider';
export class TreeSearch implements Disposable {
protected readonly tree: Tree;
protected readonly fuzzySearch: FuzzySearch;
protected readonly labelProvider: LabelProvider;
protected readonly disposables = new DisposableCollection();
protected readonly filteredNodesEmitter = new Emitter<ReadonlyArray<Readonly<TreeNode>>>();
protected _filterResult: FuzzySearch.Match<TreeNode>[] = [];
protected _filteredNodes: ReadonlyArray<Readonly<TreeNode>> = [];
protected _filteredNodesAndParents: Set<string> = new Set();
protected init(): void {
this.disposables.push(this.filteredNodesEmitter);
}
getHighlights(): Map<string, TreeDecoration.CaptionHighlight> {
return new Map(this._filterResult.map(m => [m.item.id, this.toCaptionHighlight(m)] as [string, TreeDecoration.CaptionHighlight]));
}
/**
* Resolves to all the visible tree nodes that match the search pattern.
*/
async filter(pattern: string | undefined): Promise<ReadonlyArray<Readonly<TreeNode>>> {
const { root } = this.tree;
this._filteredNodesAndParents = new Set();
if (!pattern || !root) {
this._filterResult = [];
this._filteredNodes = [];
this.fireFilteredNodesChanged(this._filteredNodes);
return [];
}
const items = [...new TopDownTreeIterator(root)];
const transform = (node: TreeNode) => this.labelProvider.getName(node);
this._filterResult = await this.fuzzySearch.filter({
items,
pattern,
transform
});
this._filteredNodes = this._filterResult.map(({ item }) => {
this.addAllParentsToFilteredSet(item);
return item;
});
this.fireFilteredNodesChanged(this._filteredNodes);
return this._filteredNodes.slice();
}
protected addAllParentsToFilteredSet(node: TreeNode): void {
let toAdd: TreeNode | undefined = node;
while (toAdd && !this._filteredNodesAndParents.has(toAdd.id)) {
this._filteredNodesAndParents.add(toAdd.id);
toAdd = toAdd.parent;
};
}
/**
* Returns with the filtered nodes after invoking the `filter` method.
*/
get filteredNodes(): ReadonlyArray<Readonly<TreeNode>> {
return this._filteredNodes.slice();
}
/**
* Event that is fired when the filtered nodes have been changed.
*/
get onFilteredNodesChanged(): Event<ReadonlyArray<Readonly<TreeNode>>> {
return this.filteredNodesEmitter.event;
}
passesFilters(node: TreeNode): boolean {
return this._filteredNodesAndParents.has(node.id);
}
dispose(): void {
this.disposables.dispose();
}
protected fireFilteredNodesChanged(nodes: ReadonlyArray<Readonly<TreeNode>>): void {
this.filteredNodesEmitter.fire(nodes);
}
protected toCaptionHighlight(match: FuzzySearch.Match<TreeNode>): TreeDecoration.CaptionHighlight {
return {
ranges: match.ranges.map(this.mapRange.bind(this))
};
}
protected mapRange(range: FuzzySearch.Range): TreeDecoration.CaptionHighlight.Range {
const { offset, length } = range;
return {
offset,
length
};
}
}