// ***************************************************************************** // 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 { FileChange, FileChangeType } from '../common/filesystem-watcher-protocol'; /** * A file change collection guarantees that only one change is reported for each URI. * * Changes are normalized according following rules: * - ADDED + ADDED => ADDED * - ADDED + UPDATED => ADDED * - ADDED + DELETED => [ADDED, DELETED] * - UPDATED + ADDED => UPDATED * - UPDATED + UPDATED => UPDATED * - UPDATED + DELETED => DELETED * - DELETED + ADDED => UPDATED * - DELETED + UPDATED => UPDATED * - DELETED + DELETED => DELETED */ export class FileChangeCollection { protected readonly changes = new Map(); push(change: FileChange): void { const changes = this.changes.get(change.uri) || []; this.normalize(changes, change); this.changes.set(change.uri, changes); } protected normalize(changes: FileChange[], change: FileChange): void { let currentType; let nextType: FileChangeType | [FileChangeType, FileChangeType] = change.type; do { const current = changes.pop(); currentType = current && current.type; nextType = this.reduce(currentType, nextType); } while (!Array.isArray(nextType) && currentType !== undefined && currentType !== nextType); const uri = change.uri; if (Array.isArray(nextType)) { changes.push(...nextType.map(type => ({ uri, type }))); } else { changes.push({ uri, type: nextType }); } } protected reduce(current: FileChangeType | undefined, change: FileChangeType): FileChangeType | [FileChangeType, FileChangeType] { if (current === undefined) { return change; } if (current === FileChangeType.ADDED) { if (change === FileChangeType.DELETED) { return [FileChangeType.ADDED, FileChangeType.DELETED]; } return FileChangeType.ADDED; } if (change === FileChangeType.DELETED) { return FileChangeType.DELETED; } return FileChangeType.UPDATED; } values(): FileChange[] { return Array.from(this.changes.values()).reduce((acc, val) => acc.concat(val), []); } }