From d6485c9c8fd64afd26eafc7cac9a61061970887d Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Mon, 22 Feb 2016 05:37:07 +0800 Subject: [PATCH 01/12] Adds navigation bar items on methods and constructors --- src/services/navigationBar.ts | 23 ++++++++++- ...ionBarItemsInsideMethodsAndConstructors.ts | 38 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f62c6cb1700..24ca0519859 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -154,6 +154,16 @@ namespace ts.NavigationBar { for (let node of nodes) { switch (node.kind) { case SyntaxKind.ClassDeclaration: + topLevelNodes.push(node); + forEach((node).members, (node) => { + if (node.kind === SyntaxKind.MethodDeclaration || + node.kind === SyntaxKind.Constructor) { + if ((node).body) { + addTopLevelNodes(((node).body).statements, topLevelNodes); + } + } + }); + break; case SyntaxKind.EnumDeclaration: case SyntaxKind.InterfaceDeclaration: topLevelNodes.push(node); @@ -193,6 +203,15 @@ namespace ts.NavigationBar { if (!isFunctionBlock(functionDeclaration.parent)) { return true; } + else { + // Except for parent functions that are methods and constructors. + const grandParentKind = functionDeclaration.parent.parent.kind; + if (grandParentKind === SyntaxKind.MethodDeclaration || + grandParentKind === SyntaxKind.Constructor) { + + return true; + } + } } } @@ -407,7 +426,7 @@ namespace ts.NavigationBar { function createModuleItem(node: ModuleDeclaration): NavigationBarItem { let moduleName = getModuleName(node); - + let childItems = getItemsWorker(getChildNodes((getInnermostModule(node).body).statements), createChildItem); return getNavigationBarItem(moduleName, @@ -422,7 +441,7 @@ namespace ts.NavigationBar { if (node.body && node.body.kind === SyntaxKind.Block) { let childItems = getItemsWorker(sortNodes((node.body).statements), createChildItem); - return getNavigationBarItem(!node.name ? "default": node.name.text , + return getNavigationBarItem(!node.name ? "default": node.name.text, ts.ScriptElementKind.functionElement, getNodeModifiers(node), [getNodeSpan(node)], diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts new file mode 100644 index 00000000000..65e3384e61b --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -0,0 +1,38 @@ +/// + +////class Class { +//// constructor() { +//// {| "itemName": "LocalFunctionInConstructor", "kind": "function", "parentName": "Class"|}function LocalFunctionInConstructor() { +//// +//// } +//// +//// {| "itemName": "LocalInterfaceInConstrcutor", "kind": "interface", "parentName": "foo"|}interface LocalInterfaceInConstrcutor { +//// } +//// +//// enum LocalEnumInConstructor { +//// {| "itemName": "LocalEnumMemberInConstructor", "kind": "property", "parentName": "LocalEnumInConstructor"|}LocalEnumMemberInConstructor, +//// } +//// } +//// method() { +//// {| "itemName": "LocalFunctionInMethod", "kind": "function", "parentName": "foo"|}function LocalFunctionInMethod() { +//// {| "itemName": "LocalFunctionInLocalFunctionInMethod", "kind": "function", "parentName": "bar"|}function LocalFunctionInLocalFunctionInMethod() { +//// +//// } +//// } +//// +//// {| "itemName": "LocalInterfaceInMethod", "kind": "interface", "parentName": "foo"|}interface LocalInterfaceInMethod { +//// } +//// +//// enum LocalEnumInMethod { +//// {| "itemName": "LocalEnumMemberInMethod", "kind": "property", "parentName": "foo"|}LocalEnumMemberInMethod, +//// } +//// } +////} + +test.markers().forEach((marker) => { + verify.getScriptLexicalStructureListContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); +}); + +// no other items +verify.getScriptLexicalStructureListCount(12); + From 1a9dadbc034b651e920d1adb7841a204b2fc0a9e Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Mon, 22 Feb 2016 05:39:19 +0800 Subject: [PATCH 02/12] Fixes typo --- src/services/navigationBar.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 24ca0519859..ae3e61ba2ca 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -198,13 +198,12 @@ namespace ts.NavigationBar { return true; } - // Or if it is not parented by another function. i.e all functions - // at module scope are 'top level'. + // Or if it is not parented by another function(except for parent functions that + // are methods and constructors). I.e all functions at module scope are 'top level'. if (!isFunctionBlock(functionDeclaration.parent)) { return true; } else { - // Except for parent functions that are methods and constructors. const grandParentKind = functionDeclaration.parent.parent.kind; if (grandParentKind === SyntaxKind.MethodDeclaration || grandParentKind === SyntaxKind.Constructor) { From 1b5b146152329e56e7ba0a298eb2cb77b0660afc Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Mon, 22 Feb 2016 05:42:32 +0800 Subject: [PATCH 03/12] Fixes if statement --- src/services/navigationBar.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index ae3e61ba2ca..62a97699537 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -156,8 +156,7 @@ namespace ts.NavigationBar { case SyntaxKind.ClassDeclaration: topLevelNodes.push(node); forEach((node).members, (node) => { - if (node.kind === SyntaxKind.MethodDeclaration || - node.kind === SyntaxKind.Constructor) { + if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) { if ((node).body) { addTopLevelNodes(((node).body).statements, topLevelNodes); } From 4d933f86ce190b1add76d54474700ed5c78863e7 Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Mon, 22 Feb 2016 11:19:38 +0800 Subject: [PATCH 04/12] Fixes method and constructor top-level --- src/services/navigationBar.ts | 30 +++++++++++++++++++ ...ionBarItemsInsideMethodsAndConstructors.ts | 1 - 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 62a97699537..44a71141694 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -158,6 +158,7 @@ namespace ts.NavigationBar { forEach((node).members, (node) => { if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) { if ((node).body) { + topLevelNodes.push(node); addTopLevelNodes(((node).body).statements, topLevelNodes); } } @@ -386,6 +387,10 @@ namespace ts.NavigationBar { case SyntaxKind.ClassDeclaration: return createClassItem(node); + + case SyntaxKind.MethodDeclaration: + case SyntaxKind.Constructor: + return createMemberFunctionLikeItem(node); case SyntaxKind.EnumDeclaration: return createEnumItem(node); @@ -449,6 +454,31 @@ namespace ts.NavigationBar { return undefined; } + + function createMemberFunctionLikeItem(node: MethodDeclaration | ConstructorDeclaration) { + if (node.body && node.body.kind === SyntaxKind.Block) { + let childItems = getItemsWorker(sortNodes((node.body).statements), createChildItem); + let scriptElementKind: string; + let memberFunctionName: string; + if (node.kind === SyntaxKind.MethodDeclaration) { + memberFunctionName = getPropertyNameForPropertyNameNode(node.name); + scriptElementKind = ts.ScriptElementKind.memberFunctionElement; + } + else { + memberFunctionName = "constructor"; + scriptElementKind = ts.ScriptElementKind.constructorImplementationElement; + } + + return getNavigationBarItem(memberFunctionName, + scriptElementKind, + getNodeModifiers(node), + [getNodeSpan(node)], + childItems, + getIndent(node)); + } + + return undefined; + } function createSourceFileItem(node: SourceFile): ts.NavigationBarItem { let childItems = getItemsWorker(getChildNodes(node.statements), createChildItem); diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts index 65e3384e61b..538e864d10c 100644 --- a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -35,4 +35,3 @@ test.markers().forEach((marker) => { // no other items verify.getScriptLexicalStructureListCount(12); - From fd2d28df0261b6b574cc415e9d47b02df21371eb Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Mon, 22 Feb 2016 12:38:14 +0800 Subject: [PATCH 05/12] Fixes new implementation --- src/services/navigationBar.ts | 23 +++++++++++++------ ...ionBarItemsInsideMethodsAndConstructors.ts | 7 +++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 44a71141694..b537a56e856 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -157,8 +157,12 @@ namespace ts.NavigationBar { topLevelNodes.push(node); forEach((node).members, (node) => { if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) { - if ((node).body) { - topLevelNodes.push(node); + type FunctionLikeMember = MethodDeclaration | ConstructorDeclaration; + if ((node).body) { + // We do not include methods that does not have child functions in it, because of duplications. + if (hasNonAnonymousFunctionDeclarations(((node).body).statements)) { + topLevelNodes.push(node); + } addTopLevelNodes(((node).body).statements, topLevelNodes); } } @@ -186,14 +190,19 @@ namespace ts.NavigationBar { } } + function hasNonAnonymousFunctionDeclarations(nodes: NodeArray) { + if (forEach(nodes, s => s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((s).name.text))) { + return true; + } + } + function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration) { if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) { // A function declaration is 'top level' if it contains any function declarations // within it. if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) { // Proper function declarations can only have identifier names - if (forEach((functionDeclaration.body).statements, - s => s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((s).name.text))) { + if (hasNonAnonymousFunctionDeclarations((functionDeclaration.body).statements)) { return true; } @@ -390,13 +399,13 @@ namespace ts.NavigationBar { case SyntaxKind.MethodDeclaration: case SyntaxKind.Constructor: - return createMemberFunctionLikeItem(node); + return createMemberFunctionLikeItem(node); case SyntaxKind.EnumDeclaration: return createEnumItem(node); case SyntaxKind.InterfaceDeclaration: - return createIterfaceItem(node); + return createInterfaceItem(node); case SyntaxKind.ModuleDeclaration: return createModuleItem(node); @@ -540,7 +549,7 @@ namespace ts.NavigationBar { getIndent(node)); } - function createIterfaceItem(node: InterfaceDeclaration): ts.NavigationBarItem { + function createInterfaceItem(node: InterfaceDeclaration): ts.NavigationBarItem { let childItems = getItemsWorker(sortNodes(removeDynamicallyNamedProperties(node)), createChildItem); return getNavigationBarItem( node.name.text, diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts index 538e864d10c..4dcca43af35 100644 --- a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -13,6 +13,7 @@ //// {| "itemName": "LocalEnumMemberInConstructor", "kind": "property", "parentName": "LocalEnumInConstructor"|}LocalEnumMemberInConstructor, //// } //// } +//// //// method() { //// {| "itemName": "LocalFunctionInMethod", "kind": "function", "parentName": "foo"|}function LocalFunctionInMethod() { //// {| "itemName": "LocalFunctionInLocalFunctionInMethod", "kind": "function", "parentName": "bar"|}function LocalFunctionInLocalFunctionInMethod() { @@ -27,6 +28,10 @@ //// {| "itemName": "LocalEnumMemberInMethod", "kind": "property", "parentName": "foo"|}LocalEnumMemberInMethod, //// } //// } +//// +//// emptyMethod() { // Non child functions method should not be duplicated +//// +//// } ////} test.markers().forEach((marker) => { @@ -34,4 +39,4 @@ test.markers().forEach((marker) => { }); // no other items -verify.getScriptLexicalStructureListCount(12); +verify.getScriptLexicalStructureListCount(17); From 99edce09bc1707ff6d41310a1174e85161355eac Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Thu, 3 Mar 2016 13:29:00 +0800 Subject: [PATCH 06/12] Fixes CR feedback --- src/services/navigationBar.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index b537a56e856..2d06177e128 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -155,18 +155,18 @@ namespace ts.NavigationBar { switch (node.kind) { case SyntaxKind.ClassDeclaration: topLevelNodes.push(node); - forEach((node).members, (node) => { - if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) { + for (const member of (node).members) { + if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.Constructor) { type FunctionLikeMember = MethodDeclaration | ConstructorDeclaration; - if ((node).body) { + if ((member).body) { // We do not include methods that does not have child functions in it, because of duplications. - if (hasNonAnonymousFunctionDeclarations(((node).body).statements)) { - topLevelNodes.push(node); + if (hasNamedFunctionDeclarations(((member).body).statements)) { + topLevelNodes.push(member); } - addTopLevelNodes(((node).body).statements, topLevelNodes); + addTopLevelNodes(((member).body).statements, topLevelNodes); } } - }); + } break; case SyntaxKind.EnumDeclaration: case SyntaxKind.InterfaceDeclaration: @@ -190,7 +190,7 @@ namespace ts.NavigationBar { } } - function hasNonAnonymousFunctionDeclarations(nodes: NodeArray) { + function hasNamedFunctionDeclarations(nodes: NodeArray) { if (forEach(nodes, s => s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((s).name.text))) { return true; } @@ -202,12 +202,11 @@ namespace ts.NavigationBar { // within it. if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) { // Proper function declarations can only have identifier names - if (hasNonAnonymousFunctionDeclarations((functionDeclaration.body).statements)) { - + if (hasNamedFunctionDeclarations((functionDeclaration.body).statements)) { return true; } - // Or if it is not parented by another function(except for parent functions that + // Or if it is not parented by another function (except for parent functions that // are methods and constructors). I.e all functions at module scope are 'top level'. if (!isFunctionBlock(functionDeclaration.parent)) { return true; From 200f162bf64c8861781dadbc2b40873255685add Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 18 Mar 2016 11:27:21 -0700 Subject: [PATCH 07/12] rename LanguageService.getSourceFile to LanguageService.getNonBoundSourceFile and mark it as internal --- src/harness/fourslash.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/server/client.ts | 2 +- src/server/editorServices.ts | 2 +- src/services/services.ts | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ba7a5f20dae..2fba2d13299 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1414,7 +1414,7 @@ namespace FourSlash { return; } - const incrementalSourceFile = this.languageService.getSourceFile(this.activeFile.fileName); + const incrementalSourceFile = this.languageService.getNonBoundSourceFile(this.activeFile.fileName); Utils.assertInvariants(incrementalSourceFile, /*parent:*/ undefined); const incrementalSyntaxDiagnostics = incrementalSourceFile.parseDiagnostics; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 2a6ed85f9cd..12bb6a470e4 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -423,7 +423,7 @@ namespace Harness.LanguageService { getProgram(): ts.Program { throw new Error("Program can not be marshaled across the shim layer."); } - getSourceFile(fileName: string): ts.SourceFile { + getNonBoundSourceFile(fileName: string): ts.SourceFile { throw new Error("SourceFile can not be marshaled across the shim layer."); } dispose(): void { this.shim.dispose({}); } diff --git a/src/server/client.ts b/src/server/client.ts index 8731c52cc72..957d36e4a3a 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -613,7 +613,7 @@ namespace ts.server { throw new Error("SourceFile objects are not serializable through the server protocol."); } - getSourceFile(fileName: string): SourceFile { + getNonBoundSourceFile(fileName: string): SourceFile { throw new Error("SourceFile objects are not serializable through the server protocol."); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 42a2bded6a5..4a299ef6fd4 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1330,7 +1330,7 @@ namespace ts.server { } isExternalModule(filename: string): boolean { - const sourceFile = this.languageService.getSourceFile(filename); + const sourceFile = this.languageService.getNonBoundSourceFile(filename); return ts.isExternalModule(sourceFile); } diff --git a/src/services/services.ts b/src/services/services.ts index 72255b55479..a634b2baf1a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1112,7 +1112,7 @@ namespace ts { getProgram(): Program; - getSourceFile(fileName: string): SourceFile; + /* @internal */ getNonBoundSourceFile(fileName: string): SourceFile; dispose(): void; } @@ -6528,7 +6528,7 @@ namespace ts { } /// Syntactic features - function getSourceFile(fileName: string): SourceFile { + function getNonBoundSourceFile(fileName: string): SourceFile { return syntaxTreeCache.getCurrentSourceFile(fileName); } @@ -7616,7 +7616,7 @@ namespace ts { getFormattingEditsAfterKeystroke, getDocCommentTemplateAtPosition, getEmitOutput, - getSourceFile, + getNonBoundSourceFile, getProgram }; } From e9b514cf8a8e5f124f245039745f5c06a431eaca Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 23 Mar 2016 12:49:34 -0700 Subject: [PATCH 08/12] Use fileName instead of Path when dealing with file systems directly --- src/compiler/sys.ts | 98 ++++++++++++++++++------------------ src/server/editorServices.ts | 8 +-- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 4cf94655b35..0bb87cc5a40 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1,8 +1,8 @@ /// 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[]; @@ -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; @@ -25,7 +25,7 @@ namespace ts { } interface WatchedFile { - filePath: Path; + fileName: string; callback: FileWatcherCallback; mtime?: Date; } @@ -35,7 +35,7 @@ namespace ts { } export interface DirectoryWatcher extends FileWatcher { - directoryPath: Path; + directoryName: string; referenceCount: number; } @@ -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); } }); } @@ -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); @@ -306,26 +306,26 @@ namespace ts { } function createWatchedFileSet() { - const dirWatchers = createFileMap(); + const dirWatchers: Map = {}; // One file can have multiple watchers - const fileWatcherCallbacks = createFileMap(); + const fileWatcherCallbacks: Map = {}; 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; } @@ -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); } } } @@ -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; @@ -549,7 +549,7 @@ 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 @@ -557,7 +557,7 @@ namespace ts { // 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))); }; } ); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 42a2bded6a5..d70a7721b16 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -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); }); } } } @@ -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), From d0545ffee85bf35accc839777065d85d032f51a7 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 24 Mar 2016 14:16:38 -0700 Subject: [PATCH 09/12] Explicitly exclude . and .. for fs.readdirSync --- src/compiler/sys.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 0bb87cc5a40..3f243b41d02 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -495,7 +495,9 @@ namespace ts { visitDirectory(path); return result; function visitDirectory(path: string) { - const files = _fs.readdirSync(path || ".").sort(); + // This filtering is necessary because on some file system node fails to exclude + // "." and "..". See https://github.com/nodejs/node/issues/4002 + const files = filter(_fs.readdirSync(path || "."), f => f !== "." && f !== "..").sort(); const directories: string[] = []; for (const current of files) { const name = combinePaths(path, current); From ddbfb7b961dfb4489ca71866a5e1d7878824ae67 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 24 Mar 2016 15:56:13 -0700 Subject: [PATCH 10/12] refactor --- src/compiler/sys.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 3f243b41d02..1ced01fb349 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -495,11 +495,14 @@ namespace ts { visitDirectory(path); return result; function visitDirectory(path: string) { - // This filtering is necessary because on some file system node fails to exclude - // "." and "..". See https://github.com/nodejs/node/issues/4002 - const files = filter(_fs.readdirSync(path || "."), f => f !== "." && f !== "..").sort(); + const files = _fs.readdirSync(path || ".").sort(); const directories: string[] = []; for (const current of files) { + // This is necessary because on some file system node fails to exclude + // "." and "..". See https://github.com/nodejs/node/issues/4002 + if (current === "." || current === "..") { + continue; + } const name = combinePaths(path, current); if (!contains(exclude, getCanonicalPath(name))) { const stat = _fs.statSync(name); From bdb741e92a701b611873231fe2b04cdfced88157 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Fri, 25 Mar 2016 12:39:43 -0700 Subject: [PATCH 11/12] Fix #7629: Check if errros are needed before reporting them in enumRelatedTo --- src/compiler/checker.ts | 12 ++-- .../reference/enumAssignmentCompat4.js | 51 +++++++++++++++ .../reference/enumAssignmentCompat4.symbols | 59 ++++++++++++++++++ .../reference/enumAssignmentCompat4.types | 62 +++++++++++++++++++ tests/cases/compiler/enumAssignmentCompat4.ts | 22 +++++++ 5 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/enumAssignmentCompat4.js create mode 100644 tests/baselines/reference/enumAssignmentCompat4.symbols create mode 100644 tests/baselines/reference/enumAssignmentCompat4.types create mode 100644 tests/cases/compiler/enumAssignmentCompat4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7254f1c2a9d..9509c286236 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5643,7 +5643,7 @@ namespace ts { } if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True; if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) { - if (result = enumRelatedTo(source, target)) { + if (result = enumRelatedTo(source, target, reportErrors)) { return result; } } @@ -6266,7 +6266,7 @@ namespace ts { return Ternary.False; } - function enumRelatedTo(source: Type, target: Type) { + function enumRelatedTo(source: Type, target: Type, reportErrors?: boolean) { if (source.symbol.name !== target.symbol.name || source.symbol.flags & SymbolFlags.ConstEnum || target.symbol.flags & SymbolFlags.ConstEnum) { @@ -6277,9 +6277,11 @@ namespace ts { if (property.flags & SymbolFlags.EnumMember) { const targetProperty = getPropertyOfType(targetEnumType, property.name); if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) { - reportError(Diagnostics.Property_0_is_missing_in_type_1, - property.name, - typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); + if (reportErrors) { + reportError(Diagnostics.Property_0_is_missing_in_type_1, + property.name, + typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); + } return Ternary.False; } } diff --git a/tests/baselines/reference/enumAssignmentCompat4.js b/tests/baselines/reference/enumAssignmentCompat4.js new file mode 100644 index 00000000000..ad28324ce0f --- /dev/null +++ b/tests/baselines/reference/enumAssignmentCompat4.js @@ -0,0 +1,51 @@ +//// [enumAssignmentCompat4.ts] +namespace M { + export enum MyEnum { + BAR + } + export var object2 = { + foo: MyEnum.BAR + }; +} + +namespace N { + export enum MyEnum { + FOO + }; + export var object1 = { + foo: MyEnum.FOO + }; +} + +let broken = [ + N.object1, + M.object2 +]; + + +//// [enumAssignmentCompat4.js] +var M; +(function (M) { + (function (MyEnum) { + MyEnum[MyEnum["BAR"] = 0] = "BAR"; + })(M.MyEnum || (M.MyEnum = {})); + var MyEnum = M.MyEnum; + M.object2 = { + foo: MyEnum.BAR + }; +})(M || (M = {})); +var N; +(function (N) { + (function (MyEnum) { + MyEnum[MyEnum["FOO"] = 0] = "FOO"; + })(N.MyEnum || (N.MyEnum = {})); + var MyEnum = N.MyEnum; + ; + N.object1 = { + foo: MyEnum.FOO + }; +})(N || (N = {})); +var broken = [ + N.object1, + M.object2 +]; diff --git a/tests/baselines/reference/enumAssignmentCompat4.symbols b/tests/baselines/reference/enumAssignmentCompat4.symbols new file mode 100644 index 00000000000..a3a80731610 --- /dev/null +++ b/tests/baselines/reference/enumAssignmentCompat4.symbols @@ -0,0 +1,59 @@ +=== tests/cases/compiler/enumAssignmentCompat4.ts === +namespace M { +>M : Symbol(M, Decl(enumAssignmentCompat4.ts, 0, 0)) + + export enum MyEnum { +>MyEnum : Symbol(MyEnum, Decl(enumAssignmentCompat4.ts, 0, 13)) + + BAR +>BAR : Symbol(MyEnum.BAR, Decl(enumAssignmentCompat4.ts, 1, 24)) + } + export var object2 = { +>object2 : Symbol(object2, Decl(enumAssignmentCompat4.ts, 4, 14)) + + foo: MyEnum.BAR +>foo : Symbol(foo, Decl(enumAssignmentCompat4.ts, 4, 26)) +>MyEnum.BAR : Symbol(MyEnum.BAR, Decl(enumAssignmentCompat4.ts, 1, 24)) +>MyEnum : Symbol(MyEnum, Decl(enumAssignmentCompat4.ts, 0, 13)) +>BAR : Symbol(MyEnum.BAR, Decl(enumAssignmentCompat4.ts, 1, 24)) + + }; +} + +namespace N { +>N : Symbol(N, Decl(enumAssignmentCompat4.ts, 7, 1)) + + export enum MyEnum { +>MyEnum : Symbol(MyEnum, Decl(enumAssignmentCompat4.ts, 9, 13)) + + FOO +>FOO : Symbol(MyEnum.FOO, Decl(enumAssignmentCompat4.ts, 10, 24)) + + }; + export var object1 = { +>object1 : Symbol(object1, Decl(enumAssignmentCompat4.ts, 13, 14)) + + foo: MyEnum.FOO +>foo : Symbol(foo, Decl(enumAssignmentCompat4.ts, 13, 26)) +>MyEnum.FOO : Symbol(MyEnum.FOO, Decl(enumAssignmentCompat4.ts, 10, 24)) +>MyEnum : Symbol(MyEnum, Decl(enumAssignmentCompat4.ts, 9, 13)) +>FOO : Symbol(MyEnum.FOO, Decl(enumAssignmentCompat4.ts, 10, 24)) + + }; +} + +let broken = [ +>broken : Symbol(broken, Decl(enumAssignmentCompat4.ts, 18, 3)) + + N.object1, +>N.object1 : Symbol(N.object1, Decl(enumAssignmentCompat4.ts, 13, 14)) +>N : Symbol(N, Decl(enumAssignmentCompat4.ts, 7, 1)) +>object1 : Symbol(N.object1, Decl(enumAssignmentCompat4.ts, 13, 14)) + + M.object2 +>M.object2 : Symbol(M.object2, Decl(enumAssignmentCompat4.ts, 4, 14)) +>M : Symbol(M, Decl(enumAssignmentCompat4.ts, 0, 0)) +>object2 : Symbol(M.object2, Decl(enumAssignmentCompat4.ts, 4, 14)) + +]; + diff --git a/tests/baselines/reference/enumAssignmentCompat4.types b/tests/baselines/reference/enumAssignmentCompat4.types new file mode 100644 index 00000000000..db7f5ce8ba2 --- /dev/null +++ b/tests/baselines/reference/enumAssignmentCompat4.types @@ -0,0 +1,62 @@ +=== tests/cases/compiler/enumAssignmentCompat4.ts === +namespace M { +>M : typeof M + + export enum MyEnum { +>MyEnum : MyEnum + + BAR +>BAR : MyEnum + } + export var object2 = { +>object2 : { foo: MyEnum; } +>{ foo: MyEnum.BAR } : { foo: MyEnum; } + + foo: MyEnum.BAR +>foo : MyEnum +>MyEnum.BAR : MyEnum +>MyEnum : typeof MyEnum +>BAR : MyEnum + + }; +} + +namespace N { +>N : typeof N + + export enum MyEnum { +>MyEnum : MyEnum + + FOO +>FOO : MyEnum + + }; + export var object1 = { +>object1 : { foo: MyEnum; } +>{ foo: MyEnum.FOO } : { foo: MyEnum; } + + foo: MyEnum.FOO +>foo : MyEnum +>MyEnum.FOO : MyEnum +>MyEnum : typeof MyEnum +>FOO : MyEnum + + }; +} + +let broken = [ +>broken : ({ foo: N.MyEnum; } | { foo: M.MyEnum; })[] +>[ N.object1, M.object2] : ({ foo: N.MyEnum; } | { foo: M.MyEnum; })[] + + N.object1, +>N.object1 : { foo: N.MyEnum; } +>N : typeof N +>object1 : { foo: N.MyEnum; } + + M.object2 +>M.object2 : { foo: M.MyEnum; } +>M : typeof M +>object2 : { foo: M.MyEnum; } + +]; + diff --git a/tests/cases/compiler/enumAssignmentCompat4.ts b/tests/cases/compiler/enumAssignmentCompat4.ts new file mode 100644 index 00000000000..0a5fc896604 --- /dev/null +++ b/tests/cases/compiler/enumAssignmentCompat4.ts @@ -0,0 +1,22 @@ +namespace M { + export enum MyEnum { + BAR + } + export var object2 = { + foo: MyEnum.BAR + }; +} + +namespace N { + export enum MyEnum { + FOO + }; + export var object1 = { + foo: MyEnum.FOO + }; +} + +let broken = [ + N.object1, + M.object2 +]; From 86b6b6c21bb4fce138888feefe465407f20c3a2a Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Sat, 26 Mar 2016 17:57:33 +0800 Subject: [PATCH 12/12] Addresses CR feedback --- src/services/navigationBar.ts | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index b8cada29cee..e9ed70b84cc 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -130,7 +130,7 @@ namespace ts.NavigationBar { return topLevelNodes; } - + function sortNodes(nodes: Node[]): Node[] { return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => { if (n1.name && n2.name) { @@ -147,7 +147,7 @@ namespace ts.NavigationBar { } }); } - + function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void { nodes = sortNodes(nodes); @@ -190,28 +190,33 @@ namespace ts.NavigationBar { } } - function hasNamedFunctionDeclarations(nodes: NodeArray) { - if (forEach(nodes, s => s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((s).name.text))) { - return true; + function hasNamedFunctionDeclarations(nodes: NodeArray): boolean { + for (let s of nodes) { + if (s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((s).name.text)) { + return true; + } } + return false; } - function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration) { + function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration): boolean { if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) { - // A function declaration is 'top level' if it contains any function declarations - // within it. + // A function declaration is 'top level' if it contains any function declarations + // within it. if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) { // Proper function declarations can only have identifier names if (hasNamedFunctionDeclarations((functionDeclaration.body).statements)) { return true; } - // Or if it is not parented by another function (except for parent functions that - // are methods and constructors). I.e all functions at module scope are 'top level'. + // Or if it is not parented by another function. I.e all functions at module scope are 'top level'. if (!isFunctionBlock(functionDeclaration.parent)) { return true; } + + // Or if it is nested inside class methods and constructors. else { + // We have made sure that a grand parent node exists with 'isFunctionBlock()' above. const grandParentKind = functionDeclaration.parent.parent.kind; if (grandParentKind === SyntaxKind.MethodDeclaration || grandParentKind === SyntaxKind.Constructor) { @@ -224,7 +229,7 @@ namespace ts.NavigationBar { return false; } - + function getItemsWorker(nodes: Node[], createItem: (n: Node) => ts.NavigationBarItem): ts.NavigationBarItem[] { let items: ts.NavigationBarItem[] = []; @@ -425,12 +430,12 @@ namespace ts.NavigationBar { let result: string[] = []; result.push(moduleDeclaration.name.text); - + while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) { moduleDeclaration = moduleDeclaration.body; result.push(moduleDeclaration.name.text); - } + } return result.join("."); } @@ -448,7 +453,7 @@ namespace ts.NavigationBar { getIndent(node)); } - function createFunctionItem(node: FunctionDeclaration) { + function createFunctionItem(node: FunctionDeclaration): ts.NavigationBarItem { if (node.body && node.body.kind === SyntaxKind.Block) { let childItems = getItemsWorker(sortNodes((node.body).statements), createChildItem); @@ -463,7 +468,7 @@ namespace ts.NavigationBar { return undefined; } - function createMemberFunctionLikeItem(node: MethodDeclaration | ConstructorDeclaration) { + function createMemberFunctionLikeItem(node: MethodDeclaration | ConstructorDeclaration): ts.NavigationBarItem { if (node.body && node.body.kind === SyntaxKind.Block) { let childItems = getItemsWorker(sortNodes((node.body).statements), createChildItem); let scriptElementKind: string; @@ -589,4 +594,4 @@ namespace ts.NavigationBar { return getTextOfNodeFromSourceText(sourceFile.text, node); } } -} \ No newline at end of file +}