UNPKG

10.8 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2017 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
18 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21 return c > 3 && r && Object.defineProperty(target, key, r), r;
22};
23var __metadata = (this && this.__metadata) || function (k, v) {
24 if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25};
26Object.defineProperty(exports, "__esModule", { value: true });
27exports.TreeImpl = exports.CompositeTreeNode = exports.TreeNode = exports.Tree = void 0;
28const inversify_1 = require("inversify");
29const event_1 = require("../../common/event");
30const disposable_1 = require("../../common/disposable");
31const cancellation_1 = require("../../common/cancellation");
32const promise_util_1 = require("../../common/promise-util");
33const common_1 = require("../../common");
34exports.Tree = Symbol('Tree');
35var TreeNode;
36(function (TreeNode) {
37 function is(node) {
38 return (0, common_1.isObject)(node) && 'id' in node && 'parent' in node;
39 }
40 TreeNode.is = is;
41 function equals(left, right) {
42 return left === right || (!!left && !!right && left.id === right.id);
43 }
44 TreeNode.equals = equals;
45 function isVisible(node) {
46 return !!node && (node.visible === undefined || node.visible);
47 }
48 TreeNode.isVisible = isVisible;
49})(TreeNode = exports.TreeNode || (exports.TreeNode = {}));
50var CompositeTreeNode;
51(function (CompositeTreeNode) {
52 function is(node) {
53 return (0, common_1.isObject)(node) && 'children' in node;
54 }
55 CompositeTreeNode.is = is;
56 function getFirstChild(parent) {
57 return parent.children[0];
58 }
59 CompositeTreeNode.getFirstChild = getFirstChild;
60 function getLastChild(parent) {
61 return parent.children[parent.children.length - 1];
62 }
63 CompositeTreeNode.getLastChild = getLastChild;
64 function isAncestor(parent, child) {
65 if (!child) {
66 return false;
67 }
68 if (TreeNode.equals(parent, child.parent)) {
69 return true;
70 }
71 return isAncestor(parent, child.parent);
72 }
73 CompositeTreeNode.isAncestor = isAncestor;
74 function indexOf(parent, node) {
75 if (!node) {
76 return -1;
77 }
78 return parent.children.findIndex(child => TreeNode.equals(node, child));
79 }
80 CompositeTreeNode.indexOf = indexOf;
81 function addChildren(parent, children) {
82 for (const child of children) {
83 addChild(parent, child);
84 }
85 return parent;
86 }
87 CompositeTreeNode.addChildren = addChildren;
88 function addChild(parent, child) {
89 const children = parent.children;
90 const index = children.findIndex(value => value.id === child.id);
91 if (index !== -1) {
92 children.splice(index, 1, child);
93 setParent(child, index, parent);
94 }
95 else {
96 children.push(child);
97 setParent(child, parent.children.length - 1, parent);
98 }
99 return parent;
100 }
101 CompositeTreeNode.addChild = addChild;
102 function removeChild(parent, child) {
103 const children = parent.children;
104 const index = children.findIndex(value => value.id === child.id);
105 if (index === -1) {
106 return;
107 }
108 children.splice(index, 1);
109 const { previousSibling, nextSibling } = child;
110 if (previousSibling) {
111 Object.assign(previousSibling, { nextSibling });
112 }
113 if (nextSibling) {
114 Object.assign(nextSibling, { previousSibling });
115 }
116 }
117 CompositeTreeNode.removeChild = removeChild;
118 function setParent(child, index, parent) {
119 const previousSibling = parent.children[index - 1];
120 const nextSibling = parent.children[index + 1];
121 Object.assign(child, { parent, previousSibling, nextSibling });
122 if (previousSibling) {
123 Object.assign(previousSibling, { nextSibling: child });
124 }
125 if (nextSibling) {
126 Object.assign(nextSibling, { previousSibling: child });
127 }
128 }
129 CompositeTreeNode.setParent = setParent;
130})(CompositeTreeNode = exports.CompositeTreeNode || (exports.CompositeTreeNode = {}));
131/**
132 * A default implementation of the tree.
133 */
134let TreeImpl = class TreeImpl {
135 constructor() {
136 this.onChangedEmitter = new event_1.Emitter();
137 this.onNodeRefreshedEmitter = new event_1.Emitter();
138 this.toDispose = new disposable_1.DisposableCollection();
139 this.onDidChangeBusyEmitter = new event_1.Emitter();
140 this.onDidChangeBusy = this.onDidChangeBusyEmitter.event;
141 this.onDidUpdateEmitter = new event_1.Emitter();
142 this.onDidUpdate = this.onDidUpdateEmitter.event;
143 this.nodes = {};
144 this.toDisposeOnSetRoot = new disposable_1.DisposableCollection();
145 this.toDispose.push(this.onChangedEmitter);
146 this.toDispose.push(this.onNodeRefreshedEmitter);
147 this.toDispose.push(this.onDidChangeBusyEmitter);
148 }
149 dispose() {
150 this.nodes = {};
151 this.toDispose.dispose();
152 }
153 get root() {
154 return this._root;
155 }
156 set root(root) {
157 this.toDisposeOnSetRoot.dispose();
158 const cancelRefresh = new cancellation_1.CancellationTokenSource();
159 this.toDisposeOnSetRoot.push(cancelRefresh);
160 this.nodes = {};
161 this._root = root;
162 this.addNode(root);
163 this.refresh(undefined, cancelRefresh.token);
164 }
165 get onChanged() {
166 return this.onChangedEmitter.event;
167 }
168 fireChanged() {
169 this.onChangedEmitter.fire(undefined);
170 }
171 get onNodeRefreshed() {
172 return this.onNodeRefreshedEmitter.event;
173 }
174 async fireNodeRefreshed(parent) {
175 await event_1.WaitUntilEvent.fire(this.onNodeRefreshedEmitter, parent);
176 this.fireChanged();
177 }
178 getNode(id) {
179 return id !== undefined ? this.nodes[id] : undefined;
180 }
181 validateNode(node) {
182 const id = !!node ? node.id : undefined;
183 return this.getNode(id);
184 }
185 async refresh(raw, cancellationToken) {
186 const parent = !raw ? this._root : this.validateNode(raw);
187 let result;
188 if (CompositeTreeNode.is(parent)) {
189 const busySource = new cancellation_1.CancellationTokenSource();
190 this.doMarkAsBusy(parent, 800, busySource.token);
191 try {
192 result = parent;
193 const children = await this.resolveChildren(parent);
194 if (cancellationToken === null || cancellationToken === void 0 ? void 0 : cancellationToken.isCancellationRequested) {
195 return;
196 }
197 result = await this.setChildren(parent, children);
198 if (cancellationToken === null || cancellationToken === void 0 ? void 0 : cancellationToken.isCancellationRequested) {
199 return;
200 }
201 }
202 finally {
203 busySource.cancel();
204 }
205 }
206 this.fireChanged();
207 return result;
208 }
209 resolveChildren(parent) {
210 return Promise.resolve(Array.from(parent.children));
211 }
212 async setChildren(parent, children) {
213 const root = this.getRootNode(parent);
214 if (this.nodes[root.id] && this.nodes[root.id] !== root) {
215 console.error(`Child node '${parent.id}' does not belong to this '${root.id}' tree.`);
216 return undefined;
217 }
218 this.removeNode(parent);
219 parent.children = children;
220 this.addNode(parent);
221 await this.fireNodeRefreshed(parent);
222 return parent;
223 }
224 removeNode(node) {
225 if (CompositeTreeNode.is(node)) {
226 node.children.forEach(child => this.removeNode(child));
227 }
228 if (node) {
229 delete this.nodes[node.id];
230 }
231 }
232 getRootNode(node) {
233 if (node.parent === undefined) {
234 return node;
235 }
236 else {
237 return this.getRootNode(node.parent);
238 }
239 }
240 addNode(node) {
241 if (node) {
242 this.nodes[node.id] = node;
243 }
244 if (CompositeTreeNode.is(node)) {
245 const { children } = node;
246 children.forEach((child, index) => {
247 CompositeTreeNode.setParent(child, index, node);
248 this.addNode(child);
249 });
250 }
251 }
252 async markAsBusy(raw, ms, token) {
253 const node = this.validateNode(raw);
254 if (node) {
255 await this.doMarkAsBusy(node, ms, token);
256 }
257 }
258 markAsChecked(node, checked) {
259 node.checkboxInfo.checked = checked;
260 this.onDidUpdateEmitter.fire([node]);
261 }
262 async doMarkAsBusy(node, ms, token) {
263 try {
264 await (0, promise_util_1.timeout)(ms, token);
265 this.doSetBusy(node);
266 token.onCancellationRequested(() => this.doResetBusy(node));
267 }
268 catch {
269 /* no-op */
270 }
271 }
272 doSetBusy(node) {
273 const oldBusy = node.busy || 0;
274 node.busy = oldBusy + 1;
275 if (oldBusy === 0) {
276 this.onDidChangeBusyEmitter.fire(node);
277 }
278 }
279 doResetBusy(node) {
280 const oldBusy = node.busy || 0;
281 if (oldBusy > 0) {
282 node.busy = oldBusy - 1;
283 if (node.busy === 0) {
284 this.onDidChangeBusyEmitter.fire(node);
285 }
286 }
287 }
288};
289TreeImpl = __decorate([
290 (0, inversify_1.injectable)(),
291 __metadata("design:paramtypes", [])
292], TreeImpl);
293exports.TreeImpl = TreeImpl;
294//# sourceMappingURL=tree.js.map
\No newline at end of file