Skip to content

Commit 5b4553d

Browse files
committed
Updated vfs
1 parent d23e5f1 commit 5b4553d

11 files changed

Lines changed: 1473 additions & 242 deletions

File tree

Jakefile.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ var typesMapOutputPath = path.join(builtLocalDirectory, 'typesMap.json');
9393

9494
var harnessCoreSources = [
9595
"harness.ts",
96-
"virtualFileSystem.ts",
96+
"collections.ts",
97+
"vpath.ts",
98+
"vfs.ts",
9799
"virtualFileSystemWithWatch.ts",
98100
"sourceMapRecorder.ts",
99101
"harnessLanguageService.ts",

src/harness/collections.ts

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/// <reference path="./harness.ts" />
2+
namespace Collections {
3+
import compareValues = ts.compareValues;
4+
import binarySearch = ts.binarySearch;
5+
import removeAt = ts.orderedRemoveItemAt;
6+
7+
const caseInsensitiveComparisonCollator = typeof Intl === "object" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;
8+
const caseSensitiveComparisonCollator = typeof Intl === "object" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "variant" }) : undefined;
9+
10+
export function compareStrings(a: string | undefined, b: string | undefined, ignoreCase?: boolean) {
11+
if (a === b) return 0;
12+
if (a === undefined) return -1;
13+
if (b === undefined) return +1;
14+
const collator = ignoreCase ? caseInsensitiveComparisonCollator : caseSensitiveComparisonCollator;
15+
if (collator) {
16+
return collator.compare(a, b);
17+
}
18+
else if (ignoreCase) {
19+
a = a.toUpperCase();
20+
b = b.toUpperCase();
21+
}
22+
return a < b ? -1 : a > b ? +1 : 0;
23+
}
24+
25+
export namespace compareStrings {
26+
export function caseSensitive(a: string | undefined, b: string | undefined) {
27+
return compareStrings(a, b, /*ignoreCase*/ false);
28+
}
29+
30+
export function caseInsensitive(a: string | undefined, b: string | undefined) {
31+
return compareStrings(a, b, /*ignoreCase*/ true);
32+
}
33+
}
34+
35+
function insertAt<T>(array: T[], index: number, value: T) {
36+
if (index === 0) {
37+
array.unshift(value);
38+
}
39+
else if (index === array.length) {
40+
array.push(value);
41+
}
42+
else {
43+
for (let i = array.length; i > index; i--) {
44+
array[i] = array[i - 1];
45+
}
46+
array[index] = value;
47+
}
48+
}
49+
50+
/**
51+
* A collection of key/value pairs sorted by key.
52+
*/
53+
export class KeyedCollection<K, V> {
54+
private _comparer: (a: K, b: K) => number;
55+
private _keys: K[] = [];
56+
private _values: V[] = [];
57+
private _order: number[] = [];
58+
private _version = 0;
59+
private _copyOnWrite = false;
60+
61+
constructor(comparer: (a: K, b: K) => number = compareValues) {
62+
this._comparer = comparer;
63+
}
64+
65+
public get size() {
66+
return this._keys.length;
67+
}
68+
69+
public has(key: K) {
70+
return binarySearch(this._keys, key, this._comparer) >= 0;
71+
}
72+
73+
public get(key: K) {
74+
const index = binarySearch(this._keys, key, this._comparer);
75+
return index >= 0 ? this._values[index] : undefined;
76+
}
77+
78+
public set(key: K, value: V) {
79+
const index = binarySearch(this._keys, key, this._comparer);
80+
if (index >= 0) {
81+
this._values[index] = value;
82+
}
83+
else {
84+
this.writePreamble();
85+
insertAt(this._keys, ~index, key);
86+
insertAt(this._values, ~index, value);
87+
insertAt(this._order, ~index, this._version);
88+
this._version++;
89+
}
90+
return this;
91+
}
92+
93+
public delete(key: K) {
94+
const index = binarySearch(this._keys, key, this._comparer);
95+
if (index >= 0) {
96+
this.writePreamble();
97+
removeAt(this._keys, index);
98+
removeAt(this._values, index);
99+
removeAt(this._order, index);
100+
this._version++;
101+
return true;
102+
}
103+
return false;
104+
}
105+
106+
public clear() {
107+
if (this.size > 0) {
108+
this.writePreamble();
109+
this._keys.length = 0;
110+
this._values.length = 0;
111+
this._order.length = 0;
112+
this._version = 0;
113+
}
114+
}
115+
116+
public forEach(callback: (value: V, key: K, collection: this) => void) {
117+
const keys = this._keys;
118+
const values = this._values;
119+
const order = this.getInsertionOrder();
120+
const version = this._version;
121+
this._copyOnWrite = true;
122+
for (let i = 0; i < order.length; i++) {
123+
callback(values[order[i]], keys[order[i]], this);
124+
}
125+
if (version === this._version) {
126+
this._copyOnWrite = false;
127+
}
128+
}
129+
130+
private writePreamble() {
131+
if (this._copyOnWrite) {
132+
this._keys = this._keys.slice();
133+
this._values = this._values.slice();
134+
this._order = this._order.slice();
135+
this._copyOnWrite = false;
136+
}
137+
}
138+
139+
private getInsertionOrder() {
140+
return this._order
141+
.map((_, i) => i)
142+
.sort((x, y) => compareValues(this._order[x], this._order[y]));
143+
}
144+
}
145+
146+
const undefinedSentinel = {};
147+
148+
/**
149+
* A collection of metadata that supports inheritance.
150+
*/
151+
export class Metadata {
152+
private _parent: Metadata | undefined;
153+
private _map: { [key: string]: any };
154+
private _version = 0;
155+
private _size = -1;
156+
private _parentVersion: number | undefined;
157+
158+
constructor(parent?: Metadata) {
159+
this._parent = parent;
160+
this._map = Object.create(parent ? parent._map : null); // tslint:disable-line:no-null-keyword
161+
}
162+
163+
public get size(): number {
164+
if (this._size === -1 || (this._parent && this._parent._version !== this._parentVersion)) {
165+
let size = 0;
166+
for (const _ in this._map) size++;
167+
this._size = size;
168+
if (this._parent) {
169+
this._parentVersion = this._parent._version;
170+
}
171+
}
172+
return this._size;
173+
}
174+
175+
public has(key: string): boolean {
176+
return this._map[key] !== undefined;
177+
}
178+
179+
public get(key: string): any {
180+
const value = this._map[key];
181+
return value === undefinedSentinel ? undefined : value;
182+
}
183+
184+
public set(key: string, value: any): this {
185+
this._map[key] = value === undefined ? undefinedSentinel : value;
186+
this._size = -1;
187+
this._version++;
188+
return this;
189+
}
190+
191+
public delete(key: string): boolean {
192+
if (this._map[key] !== undefined) {
193+
delete this._map[key];
194+
this._size = -1;
195+
this._version++;
196+
return true;
197+
}
198+
return false;
199+
}
200+
201+
public clear(): void {
202+
this._map = Object.create(this._parent ? this._parent._map : null); // tslint:disable-line:no-null-keyword
203+
this._size = -1;
204+
this._version++;
205+
}
206+
207+
public forEach(callback: (value: any, key: string, map: this) => void) {
208+
for (const key in this._map) {
209+
callback(this._map[key], key, this);
210+
}
211+
}
212+
}
213+
}

src/harness/harness.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/// <reference path="..\server\client.ts" />
2121
/// <reference path="sourceMapRecorder.ts"/>
2222
/// <reference path="runnerbase.ts"/>
23-
/// <reference path="virtualFileSystem.ts" />
23+
/// <reference path="vfs.ts" />
2424
/// <reference types="node" />
2525
/// <reference types="mocha" />
2626
/// <reference types="chai" />
@@ -743,8 +743,8 @@ namespace Harness {
743743
fs.addFile(file);
744744
}
745745
return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), depth, path => {
746-
const entry = fs.traversePath(path);
747-
if (entry && entry.isDirectory()) {
746+
const entry = fs.getEntry(path);
747+
if (entry instanceof Utils.VirtualDirectory) {
748748
const directory = <Utils.VirtualDirectory>entry;
749749
return {
750750
files: ts.map(directory.getFiles(), f => f.name),

src/harness/harnessLanguageService.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ namespace Harness.LanguageService {
123123
}
124124

125125
export class LanguageServiceAdapterHost {
126-
protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false);
126+
protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/ false);
127127

128128
constructor(protected cancellationToken = DefaultHostCancellationToken.Instance,
129129
protected settings = ts.getDefaultCompilerOptions()) {
@@ -135,9 +135,9 @@ namespace Harness.LanguageService {
135135

136136
public getFilenames(): string[] {
137137
const fileNames: string[] = [];
138-
for (const virtualEntry of this.virtualFileSystem.getAllFileEntries()) {
139-
const scriptInfo = virtualEntry.content;
140-
if (scriptInfo.isRootFile) {
138+
for (const virtualEntry of this.virtualFileSystem.getFiles({ recursive: true })) {
139+
const scriptInfo = virtualEntry.metadata.get("scriptInfo");
140+
if (scriptInfo && scriptInfo.isRootFile) {
141141
// only include root files here
142142
// usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir.
143143
fileNames.push(scriptInfo.fileName);
@@ -147,18 +147,20 @@ namespace Harness.LanguageService {
147147
}
148148

149149
public getScriptInfo(fileName: string): ScriptInfo {
150-
const fileEntry = this.virtualFileSystem.traversePath(fileName);
151-
return fileEntry && fileEntry.isFile() ? (<Utils.VirtualFile>fileEntry).content : undefined;
150+
const fileEntry = this.virtualFileSystem.getFile(fileName);
151+
return fileEntry ? fileEntry.metadata.get("scriptInfo") : undefined;
152152
}
153153

154154
public addScript(fileName: string, content: string, isRootFile: boolean): void {
155-
this.virtualFileSystem.addFile(fileName, new ScriptInfo(fileName, content, isRootFile));
155+
this.virtualFileSystem.addFile(fileName, content, { overwrite: true }).metadata.set("scriptInfo", new ScriptInfo(fileName, content, isRootFile));
156156
}
157157

158158
public editScript(fileName: string, start: number, end: number, newText: string) {
159-
const script = this.getScriptInfo(fileName);
160-
if (script !== undefined) {
159+
const file = this.virtualFileSystem.getFile(fileName);
160+
const script = file && file.metadata.get("scriptInfo") as ScriptInfo;
161+
if (script) {
161162
script.editContent(start, end, newText);
163+
file.setContent(script.content);
162164
return;
163165
}
164166

@@ -185,9 +187,9 @@ namespace Harness.LanguageService {
185187
getCompilationSettings() { return this.settings; }
186188
getCancellationToken() { return this.cancellationToken; }
187189
getDirectories(path: string): string[] {
188-
const dir = this.virtualFileSystem.traversePath(path);
189-
if (dir && dir.isDirectory()) {
190-
return ts.map((<Utils.VirtualDirectory>dir).getDirectories(), (d) => ts.combinePaths(path, d.name));
190+
const dir = this.virtualFileSystem.getDirectory(path);
191+
if (dir) {
192+
return ts.map(dir.getDirectories(), (d) => ts.combinePaths(path, d.name));
191193
}
192194
return [];
193195
}

src/harness/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@
9797
"./parallel/host.ts",
9898
"./parallel/worker.ts",
9999
"runner.ts",
100+
"collections.ts",
101+
"vpath.ts",
102+
"vfs.ts",
100103
"virtualFileSystemWithWatch.ts",
101104
"../server/protocol.ts",
102105
"../server/session.ts",

src/harness/unittests/configurationExtension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// <reference path="..\harness.ts" />
2-
/// <reference path="..\virtualFileSystem.ts" />
2+
/// <reference path="..\vfs.ts" />
33

44
namespace ts {
55
const testContentsJson = createMapFromTemplate({

src/harness/unittests/matchFiles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// <reference path="..\harness.ts" />
2-
/// <reference path="..\virtualFileSystem.ts" />
2+
/// <reference path="..\vfs.ts" />
33

44
namespace ts {
55
const caseInsensitiveBasePath = "c:/dev/";

0 commit comments

Comments
 (0)