perf: do not run into O(n^2) when adding child to a folder
This commit is contained in:
parent
55ccfe43ce
commit
c4e9196d21
3 changed files with 12 additions and 71 deletions
|
@ -367,6 +367,7 @@ export class ExplorerView extends CollapsibleViewletView {
|
|||
|
||||
// Add the new file to its parent (Model)
|
||||
let childElement = FileStat.create(addedElement);
|
||||
parentElement.removeChild(childElement); // make sure to remove any previous version of the file if any
|
||||
parentElement.addChild(childElement);
|
||||
|
||||
let refreshPromise = () => {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
'use strict';
|
||||
|
||||
import assert = require('vs/base/common/assert');
|
||||
import types = require('vs/base/common/types');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {isLinux} from 'vs/base/common/platform';
|
||||
import paths = require('vs/base/common/paths');
|
||||
|
@ -54,7 +53,7 @@ export class FileStat implements IFileStat {
|
|||
}
|
||||
|
||||
public static create(raw: IFileStat, resolveTo?: URI[]): FileStat {
|
||||
let stat = new FileStat(raw.resource, raw.isDirectory, raw.hasChildren, raw.name, raw.mime, raw.mtime, raw.etag);
|
||||
const stat = new FileStat(raw.resource, raw.isDirectory, raw.hasChildren, raw.name, raw.mime, raw.mtime, raw.etag);
|
||||
|
||||
// Recursively add children if present
|
||||
if (stat.isDirectory) {
|
||||
|
@ -69,7 +68,7 @@ export class FileStat implements IFileStat {
|
|||
// Recurse into children
|
||||
if (raw.children) {
|
||||
for (let i = 0, len = raw.children.length; i < len; i++) {
|
||||
let child = FileStat.create(raw.children[i], resolveTo);
|
||||
const child = FileStat.create(raw.children[i], resolveTo);
|
||||
child.parent = stat;
|
||||
stat.children.push(child);
|
||||
stat.hasChildren = stat.children.length > 0;
|
||||
|
@ -89,7 +88,7 @@ export class FileStat implements IFileStat {
|
|||
assert.ok(disk.resource.toString() === local.resource.toString(), 'Merging only supported for stats with the same resource');
|
||||
|
||||
// Stop merging when a folder is not resolved to avoid loosing local data
|
||||
let mergingDirectories = disk.isDirectory || local.isDirectory;
|
||||
const mergingDirectories = disk.isDirectory || local.isDirectory;
|
||||
if (mergingDirectories && local.isDirectoryResolved && !disk.isDirectoryResolved) {
|
||||
return;
|
||||
}
|
||||
|
@ -107,7 +106,7 @@ export class FileStat implements IFileStat {
|
|||
if (mergingDirectories && disk.isDirectoryResolved) {
|
||||
|
||||
// Map resource => stat
|
||||
let oldLocalChildren: { [resource: string]: FileStat; } = Object.create(null);
|
||||
const oldLocalChildren: { [resource: string]: FileStat; } = Object.create(null);
|
||||
local.children.forEach((localChild: FileStat) => {
|
||||
oldLocalChildren[localChild.resource.toString()] = localChild;
|
||||
});
|
||||
|
@ -117,7 +116,7 @@ export class FileStat implements IFileStat {
|
|||
|
||||
// Merge received children
|
||||
disk.children.forEach((diskChild: FileStat) => {
|
||||
let formerLocalChild = oldLocalChildren[diskChild.resource.toString()];
|
||||
const formerLocalChild = oldLocalChildren[diskChild.resource.toString()];
|
||||
|
||||
// Existing child: merge
|
||||
if (formerLocalChild) {
|
||||
|
@ -139,7 +138,7 @@ export class FileStat implements IFileStat {
|
|||
* Returns a deep copy of this model object.
|
||||
*/
|
||||
public clone(): FileStat {
|
||||
let stat = new FileStat(URI.parse(this.resource.toString()), this.isDirectory, this.hasChildren, this.name, this.mime, this.mtime, this.etag);
|
||||
const stat = new FileStat(URI.parse(this.resource.toString()), this.isDirectory, this.hasChildren, this.name, this.mime, this.mtime, this.etag);
|
||||
stat.isDirectoryResolved = this.isDirectoryResolved;
|
||||
|
||||
if (this.parent) {
|
||||
|
@ -159,10 +158,6 @@ export class FileStat implements IFileStat {
|
|||
* Adds a child element to this folder.
|
||||
*/
|
||||
public addChild(child: FileStat): void {
|
||||
assert.ok(this.isDirectory, 'Can only add a child to a folder');
|
||||
|
||||
// Overwrite a previous child with the same name
|
||||
this.removeChild(child);
|
||||
|
||||
// Inherit some parent properties to child
|
||||
child.parent = this;
|
||||
|
@ -179,11 +174,8 @@ export class FileStat implements IFileStat {
|
|||
* @param type the type of stat to check for.
|
||||
*/
|
||||
public hasChild(name: string, ignoreCase?: boolean, type: StatType = StatType.ANY): boolean {
|
||||
assert.ok(this.isDirectory, 'Can only call hasChild on a directory');
|
||||
assert.ok(types.isString(name), 'Expected parameter of type String');
|
||||
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
let child = this.children[i];
|
||||
const child = this.children[i];
|
||||
if ((type === StatType.FILE && child.isDirectory) || (type === StatType.FOLDER && !child.isDirectory)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -206,9 +198,6 @@ export class FileStat implements IFileStat {
|
|||
* Removes a child element from this folder.
|
||||
*/
|
||||
public removeChild(child: FileStat): void {
|
||||
assert.ok(this.isDirectory, 'Can only remove a child from a directory');
|
||||
assert.ok(!!this.children, 'Expected children for directory but found none: ' + this.resource.fsPath);
|
||||
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
if (this.children[i].resource.toString() === child.resource.toString()) {
|
||||
this.children.splice(i, 1);
|
||||
|
@ -223,8 +212,6 @@ export class FileStat implements IFileStat {
|
|||
* Moves this element under a new parent element.
|
||||
*/
|
||||
public move(newParent: FileStat, fnBetweenStates?: (callback: () => void) => void, fnDone?: () => void): void {
|
||||
assert.ok(newParent.isDirectory, 'Can only move an element into a directory');
|
||||
|
||||
if (!fnBetweenStates) {
|
||||
fnBetweenStates = (cb: () => void) => { cb(); };
|
||||
}
|
||||
|
@ -232,6 +219,7 @@ export class FileStat implements IFileStat {
|
|||
this.parent.removeChild(this);
|
||||
|
||||
fnBetweenStates(() => {
|
||||
newParent.removeChild(this); // make sure to remove any previous version of the file if any
|
||||
newParent.addChild(this);
|
||||
this.updateResource(true);
|
||||
if (fnDone) {
|
||||
|
@ -284,7 +272,7 @@ export class FileStat implements IFileStat {
|
|||
}
|
||||
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
let child = this.children[i];
|
||||
const child = this.children[i];
|
||||
|
||||
if (this.fileResourceEquals(resource, child.resource)) {
|
||||
return child;
|
||||
|
@ -341,7 +329,7 @@ export class NewStatPlaceholder extends FileStat {
|
|||
* Returns a deep copy of this model object.
|
||||
*/
|
||||
public clone(): NewStatPlaceholder {
|
||||
let stat = new NewStatPlaceholder(this.isDirectory);
|
||||
const stat = new NewStatPlaceholder(this.isDirectory);
|
||||
stat.parent = this.parent;
|
||||
|
||||
return stat;
|
||||
|
@ -372,9 +360,7 @@ export class NewStatPlaceholder extends FileStat {
|
|||
}
|
||||
|
||||
public static addNewStatPlaceholder(parent: FileStat, isDirectory: boolean): NewStatPlaceholder {
|
||||
assert.ok(parent.isDirectory, 'Can only add a child to a folder');
|
||||
|
||||
let child = new NewStatPlaceholder(isDirectory);
|
||||
const child = new NewStatPlaceholder(isDirectory);
|
||||
|
||||
// Inherit some parent properties to child
|
||||
child.parent = parent;
|
||||
|
|
|
@ -44,32 +44,11 @@ suite('Files - View Model', () => {
|
|||
test('Add and Remove Child, check for hasChild', function () {
|
||||
let d = new Date().getTime();
|
||||
let s = createStat('/path/to/stat', 'sName', true, false, 8096, d);
|
||||
let s2 = createStat('/path/to/stat2', 'sName2', false, false, 8096, d);
|
||||
|
||||
let child1 = createStat('/path/to/stat/foo', 'foo', true, false, 8096, d);
|
||||
let child2 = createStat('/path/to/stat/bar.html', 'bar', false, false, 8096, d);
|
||||
let child4 = createStat('/otherpath/to/other/otherbar.html', 'otherbar.html', false, false, 8096, d);
|
||||
|
||||
assert.throws(function () {
|
||||
s2.addChild(child1); // Can not add into non directory
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s2.addChild(null);
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s2.hasChild(child1.name);
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s2.removeChild(child1);
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s.hasChild(null);
|
||||
});
|
||||
|
||||
assert(!s.hasChild(child1.name));
|
||||
assert(!s.hasChild(child2.name));
|
||||
|
||||
|
@ -105,18 +84,6 @@ suite('Files - View Model', () => {
|
|||
s2.addChild(s3);
|
||||
s3.addChild(s4);
|
||||
|
||||
assert.throws(function () {
|
||||
s4.move(null);
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s2.move(s4); // Can not move into a file
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s1.move(s3); // Can not move root
|
||||
});
|
||||
|
||||
s4.move(s1);
|
||||
|
||||
assert.strictEqual(s3.children.length, 0);
|
||||
|
@ -148,19 +115,6 @@ suite('Files - View Model', () => {
|
|||
let s2 = createStat('/path', 'path', true, false, 8096, d);
|
||||
let s3 = createStat('/path/to', 'to', true, false, 8096, d);
|
||||
let s4 = createStat('/path/to/stat', 'stat', true, false, 8096, d);
|
||||
let s5 = createStat('/path/to/stat', 'stat', true, false, 8096, d);
|
||||
|
||||
assert.throws(function () {
|
||||
s2.rename(null);
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s1.rename(s2); // Can not rename root
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
s4.rename(s5); // Can not rename to stat from different workspace
|
||||
});
|
||||
|
||||
s1.addChild(s2);
|
||||
s2.addChild(s3);
|
||||
|
|
Loading…
Reference in a new issue