Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 49 additions & 49 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// <reference path="core.ts"/>

namespace ts {
export type FileWatcherCallback = (path: string, removed?: boolean) => void;
export type DirectoryWatcherCallback = (path: string) => void;
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
export type DirectoryWatcherCallback = (directoryName: string) => void;

export interface System {
args: string[];
Expand All @@ -11,7 +11,7 @@ namespace ts {
write(s: string): void;
readFile(path: string, encoding?: string): string;
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
watchFile?(path: Path, callback: FileWatcherCallback): FileWatcher;
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
resolvePath(path: string): string;
fileExists(path: string): boolean;
Expand All @@ -25,7 +25,7 @@ namespace ts {
}

interface WatchedFile {
filePath: Path;
fileName: string;
callback: FileWatcherCallback;
mtime?: Date;
}
Expand All @@ -35,7 +35,7 @@ namespace ts {
}

export interface DirectoryWatcher extends FileWatcher {
directoryPath: Path;
directoryName: string;
referenceCount: number;
}

Expand Down Expand Up @@ -244,13 +244,13 @@ namespace ts {
return;
}

_fs.stat(watchedFile.filePath, (err: any, stats: any) => {
_fs.stat(watchedFile.fileName, (err: any, stats: any) => {
if (err) {
watchedFile.callback(watchedFile.filePath);
watchedFile.callback(watchedFile.fileName);
}
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
watchedFile.mtime = getModifiedTime(watchedFile.filePath);
watchedFile.callback(watchedFile.filePath, watchedFile.mtime.getTime() === 0);
watchedFile.mtime = getModifiedTime(watchedFile.fileName);
watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0);
}
});
}
Expand Down Expand Up @@ -278,11 +278,11 @@ namespace ts {
}, interval);
}

function addFile(filePath: Path, callback: FileWatcherCallback): WatchedFile {
function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile {
const file: WatchedFile = {
filePath,
fileName,
callback,
mtime: getModifiedTime(filePath)
mtime: getModifiedTime(fileName)
};

watchedFiles.push(file);
Expand All @@ -306,26 +306,26 @@ namespace ts {
}

function createWatchedFileSet() {
const dirWatchers = createFileMap<DirectoryWatcher>();
const dirWatchers: Map<DirectoryWatcher> = {};
// One file can have multiple watchers
const fileWatcherCallbacks = createFileMap<FileWatcherCallback[]>();
const fileWatcherCallbacks: Map<FileWatcherCallback[]> = {};
return { addFile, removeFile };

function reduceDirWatcherRefCountForFile(filePath: Path) {
const dirPath = getDirectoryPath(filePath);
if (dirWatchers.contains(dirPath)) {
const watcher = dirWatchers.get(dirPath);
function reduceDirWatcherRefCountForFile(fileName: string) {
const dirName = getDirectoryPath(fileName);
if (hasProperty(dirWatchers, dirName)) {
const watcher = dirWatchers[dirName];
watcher.referenceCount -= 1;
if (watcher.referenceCount <= 0) {
watcher.close();
dirWatchers.remove(dirPath);
delete dirWatchers[dirName];
}
}
}

function addDirWatcher(dirPath: Path): void {
if (dirWatchers.contains(dirPath)) {
const watcher = dirWatchers.get(dirPath);
function addDirWatcher(dirPath: string): void {
if (hasProperty(dirWatchers, dirPath)) {
const watcher = dirWatchers[dirPath];
watcher.referenceCount += 1;
return;
}
Expand All @@ -336,52 +336,52 @@ namespace ts {
(eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath)
);
watcher.referenceCount = 1;
dirWatchers.set(dirPath, watcher);
dirWatchers[dirPath] = watcher;
return;
}

function addFileWatcherCallback(filePath: Path, callback: FileWatcherCallback): void {
if (fileWatcherCallbacks.contains(filePath)) {
fileWatcherCallbacks.get(filePath).push(callback);
function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void {
if (hasProperty(fileWatcherCallbacks, filePath)) {
fileWatcherCallbacks[filePath].push(callback);
}
else {
fileWatcherCallbacks.set(filePath, [callback]);
fileWatcherCallbacks[filePath] = [callback];
}
}

function addFile(filePath: Path, callback: FileWatcherCallback): WatchedFile {
addFileWatcherCallback(filePath, callback);
addDirWatcher(getDirectoryPath(filePath));
function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile {
addFileWatcherCallback(fileName, callback);
addDirWatcher(getDirectoryPath(fileName));

return { filePath, callback };
return { fileName, callback };
}

function removeFile(watchedFile: WatchedFile) {
removeFileWatcherCallback(watchedFile.filePath, watchedFile.callback);
reduceDirWatcherRefCountForFile(watchedFile.filePath);
removeFileWatcherCallback(watchedFile.fileName, watchedFile.callback);
reduceDirWatcherRefCountForFile(watchedFile.fileName);
}

function removeFileWatcherCallback(filePath: Path, callback: FileWatcherCallback) {
if (fileWatcherCallbacks.contains(filePath)) {
const newCallbacks = copyListRemovingItem(callback, fileWatcherCallbacks.get(filePath));
function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) {
if (hasProperty(fileWatcherCallbacks, filePath)) {
const newCallbacks = copyListRemovingItem(callback, fileWatcherCallbacks[filePath]);
if (newCallbacks.length === 0) {
fileWatcherCallbacks.remove(filePath);
delete fileWatcherCallbacks[filePath];
}
else {
fileWatcherCallbacks.set(filePath, newCallbacks);
fileWatcherCallbacks[filePath] = newCallbacks;
}
}
}

function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: Path) {
function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) {
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
const filePath = typeof relativeFileName !== "string"
const fileName = typeof relativeFileName !== "string"
? undefined
: toPath(relativeFileName, baseDirPath, createGetCanonicalFileName(sys.useCaseSensitiveFileNames));
: ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath);
// Some applications save a working file via rename operations
if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks.contains(filePath)) {
for (const fileCallback of fileWatcherCallbacks.get(filePath)) {
fileCallback(filePath);
if ((eventName === "change" || eventName === "rename") && hasProperty(fileWatcherCallbacks, fileName)) {
for (const fileCallback of fileWatcherCallbacks[fileName]) {
fileCallback(fileName);
}
}
}
Expand Down Expand Up @@ -526,18 +526,18 @@ namespace ts {
},
readFile,
writeFile,
watchFile: (filePath, callback) => {
watchFile: (fileName, callback) => {
// Node 4.0 stabilized the `fs.watch` function on Windows which avoids polling
// and is more efficient than `fs.watchFile` (ref: https://github.com/nodejs/node/pull/2649
// and https://github.com/Microsoft/TypeScript/issues/4643), therefore
// if the current node.js version is newer than 4, use `fs.watch` instead.
const watchSet = isNode4OrLater() ? watchedFileSet : pollingWatchedFileSet;
const watchedFile = watchSet.addFile(filePath, callback);
const watchedFile = watchSet.addFile(fileName, callback);
return {
close: () => watchSet.removeFile(watchedFile)
};
},
watchDirectory: (path, callback, recursive) => {
watchDirectory: (directoryName, callback, recursive) => {
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
let options: any;
Expand All @@ -549,15 +549,15 @@ namespace ts {
}

return _fs.watch(
path,
directoryName,
options,
(eventName: string, relativeFileName: string) => {
// In watchDirectory we only care about adding and removing files (when event name is
// "rename"); changes made within files are handled by corresponding fileWatchers (when
// event name is "change")
if (eventName === "rename") {
// When deleting a file, the passed baseFileName is null
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(path, relativeFileName)));
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
};
}
);
Expand Down
8 changes: 2 additions & 6 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1006,9 +1006,7 @@ namespace ts.server {
info.setFormatOptions(this.getFormatCodeOptions());
this.filenameToScriptInfo[fileName] = info;
if (!info.isOpen) {
info.fileWatcher = this.host.watchFile(
toPath(fileName, fileName, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)),
_ => { this.watchedFileChanged(fileName); });
info.fileWatcher = this.host.watchFile(fileName, _ => { this.watchedFileChanged(fileName); });
}
}
}
Expand Down Expand Up @@ -1227,9 +1225,7 @@ namespace ts.server {
}
}
project.finishGraph();
project.projectFileWatcher = this.host.watchFile(
toPath(configFilename, configFilename, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)),
_ => this.watchedProjectConfigFileChanged(project));
project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project));
this.log("Add recursive watcher for: " + ts.getDirectoryPath(configFilename));
project.directoryWatcher = this.host.watchDirectory(
ts.getDirectoryPath(configFilename),
Expand Down