Files
azuredatastudio/src/vs/platform/files/node/watcher/watcher.ts
Charles Gagnon 3cb2f552a6 Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 (#15681)
* Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898

* Fixes and cleanup

* Distro

* Fix hygiene yarn

* delete no yarn lock changes file

* Fix hygiene

* Fix layer check

* Fix CI

* Skip lib checks

* Remove tests deleted in vs code

* Fix tests

* Distro

* Fix tests and add removed extension point

* Skip failing notebook tests for now

* Disable broken tests and cleanup build folder

* Update yarn.lock and fix smoke tests

* Bump sqlite

* fix contributed actions and file spacing

* Fix user data path

* Update yarn.locks

Co-authored-by: ADS Merger <karlb@microsoft.com>
2021-06-17 08:17:11 -07:00

110 lines
3.4 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI as uri } from 'vs/base/common/uri';
import { FileChangeType, isParent, IFileChange } from 'vs/platform/files/common/files';
import { isLinux } from 'vs/base/common/platform';
export interface IDiskFileChange {
type: FileChangeType;
path: string;
}
export interface ILogMessage {
type: 'trace' | 'warn' | 'error' | 'info' | 'debug';
message: string;
}
export function toFileChanges(changes: IDiskFileChange[]): IFileChange[] {
return changes.map(change => ({
type: change.type,
resource: uri.file(change.path)
}));
}
export function normalizeFileChanges(changes: IDiskFileChange[]): IDiskFileChange[] {
// Build deltas
const normalizer = new EventNormalizer();
for (const event of changes) {
normalizer.processEvent(event);
}
return normalizer.normalize();
}
class EventNormalizer {
private normalized: IDiskFileChange[] = [];
private mapPathToChange: Map<string, IDiskFileChange> = new Map();
processEvent(event: IDiskFileChange): void {
const existingEvent = this.mapPathToChange.get(event.path);
// Event path already exists
if (existingEvent) {
const currentChangeType = existingEvent.type;
const newChangeType = event.type;
// ignore CREATE followed by DELETE in one go
if (currentChangeType === FileChangeType.ADDED && newChangeType === FileChangeType.DELETED) {
this.mapPathToChange.delete(event.path);
this.normalized.splice(this.normalized.indexOf(existingEvent), 1);
}
// flatten DELETE followed by CREATE into CHANGE
else if (currentChangeType === FileChangeType.DELETED && newChangeType === FileChangeType.ADDED) {
existingEvent.type = FileChangeType.UPDATED;
}
// Do nothing. Keep the created event
else if (currentChangeType === FileChangeType.ADDED && newChangeType === FileChangeType.UPDATED) { }
// Otherwise apply change type
else {
existingEvent.type = newChangeType;
}
}
// Otherwise store new
else {
this.normalized.push(event);
this.mapPathToChange.set(event.path, event);
}
}
normalize(): IDiskFileChange[] {
const addedChangeEvents: IDiskFileChange[] = [];
const deletedPaths: string[] = [];
// This algorithm will remove all DELETE events up to the root folder
// that got deleted if any. This ensures that we are not producing
// DELETE events for each file inside a folder that gets deleted.
//
// 1.) split ADD/CHANGE and DELETED events
// 2.) sort short deleted paths to the top
// 3.) for each DELETE, check if there is a deleted parent and ignore the event in that case
return this.normalized.filter(e => {
if (e.type !== FileChangeType.DELETED) {
addedChangeEvents.push(e);
return false; // remove ADD / CHANGE
}
return true; // keep DELETE
}).sort((e1, e2) => {
return e1.path.length - e2.path.length; // shortest path first
}).filter(e => {
if (deletedPaths.some(d => isParent(e.path, d, !isLinux /* ignorecase */))) {
return false; // DELETE is ignored if parent is deleted already
}
// otherwise mark as deleted
deletedPaths.push(e.path);
return true;
}).concat(addedChangeEvents);
}
}