From efda30c6de33767231a5e940311360d391424388 Mon Sep 17 00:00:00 2001 From: Herrington Darkholme Date: Tue, 4 Oct 2016 20:32:13 +0800 Subject: [PATCH 1/6] remove redundant type assertion --- src/compiler/binder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2fe7d30ab56..13cf861100a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2207,9 +2207,9 @@ namespace ts { if (currentFlow) { node.flowNode = currentFlow; } - checkStrictModeFunctionName(node); - const bindingName = (node).name ? (node).name.text : "__function"; - return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); + checkStrictModeFunctionName(node); + const bindingName = node.name ? node.name.text : "__function"; + return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); } function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { From 5ce22801a71571f52936fa16360a40f6870a025e Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 10 Oct 2016 11:11:06 -0700 Subject: [PATCH 2/6] Port fix from PR#11268 --- src/services/navigationBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 5c8c47e9666..0397ae73229 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -324,7 +324,7 @@ namespace ts.NavigationBar { } // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times. - const collator: { compare(a: string, b: string): number } = typeof Intl === "undefined" ? undefined : new Intl.Collator(); + const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator() : undefined; // Intl is missing in Safari, and node 0.10 treats "a" as greater than "B". const localeCompareIsCorrect = collator && collator.compare("a", "B") < 0; const localeCompareFix: (a: string, b: string) => number = localeCompareIsCorrect ? collator.compare : function(a, b) { From 997acaec7a08321019729fd5aff3bd6731e98211 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 10 Oct 2016 16:04:14 -0700 Subject: [PATCH 3/6] update documentation in protocol.d.ts (#11501) update documentation in protocol.d.ts --- src/server/protocol.d.ts | 292 ++++++++++++++++++++++++++++++++++++--- src/server/session.ts | 8 +- 2 files changed, 277 insertions(+), 23 deletions(-) diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index bad403deaf9..cd6f12453d1 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -98,19 +98,38 @@ declare namespace ts.server.protocol { projectFileName?: string; } + /** + * A request to get TODO comments from the file + */ export interface TodoCommentRequest extends FileRequest { arguments: TodoCommentRequestArgs; } + /** + * Arguments for TodoCommentRequest request. + */ export interface TodoCommentRequestArgs extends FileRequestArgs { + /** + * Array of target TodoCommentDescriptors that describes TODO comments to be found + */ descriptors: TodoCommentDescriptor[]; } + /** + * A request to get indentation for a location in file + */ export interface IndentationRequest extends FileLocationRequest { arguments: IndentationRequestArgs; } + /** + * Arguments for IndentationRequest request. + */ export interface IndentationRequestArgs extends FileLocationRequestArgs { + /** + * An optional set of settings to be used when computing indentation. + * If argument is omitted - then it will use settings for file that were previously set via 'configure' request or global settings. + */ options?: EditorSettings; } @@ -125,17 +144,26 @@ declare namespace ts.server.protocol { } /** - * A request to get the project information of the current file + * A request to get the project information of the current file. */ export interface ProjectInfoRequest extends Request { arguments: ProjectInfoRequestArgs; } - export interface ProjectRequest extends Request { - arguments: ProjectRequestArgs; + /** + * A request to retrieve compiler options diagnostics for a project + */ + export interface CompilerOptionsDiagnosticsRequest extends Request { + arguments: CompilerOptionsDiagnosticsRequestArgs; } - export interface ProjectRequestArgs { + /** + * Arguments for CompilerOptionsDiagnosticsRequest request. + */ + export interface CompilerOptionsDiagnosticsRequestArgs { + /** + * Name of the project to retrieve compiler options diagnostics. + */ projectFileName: string; } @@ -158,6 +186,11 @@ declare namespace ts.server.protocol { languageServiceDisabled?: boolean; } + /** + * Represents diagnostic info that includes location of diagnostic in two forms + * - start position and length of the error span + * - startLocation and endLocation - a pair of Location objects that store start/end line and offset of the error span. + */ export interface DiagnosticWithLinePosition { message: string; start: number; @@ -210,13 +243,25 @@ declare namespace ts.server.protocol { arguments: FileLocationRequestArgs; } - export interface FileSpanRequestArgs extends FileRequestArgs { - start: number; - length: number; + /** + * A request to get semantic diagnostics for a span in the file + */ + export interface SemanticDiagnosticsRequest extends FileRequest { + arguments: SemanticDiagnosticsRequestArgs; } - export interface FileSpanRequest extends FileRequest { - arguments: FileSpanRequestArgs; + /** + * Arguments for SemanticDiagnosticsRequest request. + */ + export interface SemanticDiagnosticsRequestArgs extends FileRequestArgs { + /** + * Start position of the span. + */ + start: number; + /** + * Length of the span. + */ + length: number; } /** @@ -308,11 +353,20 @@ declare namespace ts.server.protocol { body?: FileSpan[]; } + /** + * Request to get brace completion for a location in the file. + */ export interface BraceCompletionRequest extends FileLocationRequest { arguments: BraceCompletionRequestArgs; } + /** + * Argument for BraceCompletionRequest request. + */ export interface BraceCompletionRequestArgs extends FileLocationRequestArgs { + /** + * Kind of opening brace + */ openingBrace: string; } @@ -344,10 +398,17 @@ declare namespace ts.server.protocol { arguments: DocumentHighlightsRequestArgs; } + /** + * Span augmented with extra information that denotes the kind of the highlighting to be used for span. + * Kind is taken from HighlightSpanKind type. + */ export interface HighlightSpan extends TextSpan { kind: string; } + /** + * Represents a set of highligh spans for a give name + */ export interface DocumentHighlightsItem { /** * File containing highlight spans. @@ -360,6 +421,9 @@ declare namespace ts.server.protocol { highlightSpans: HighlightSpan[]; } + /** + * Response for a DocumentHighlightsRequest request. + */ export interface DocumentHighlightsResponse extends Response { body?: DocumentHighlightsItem[]; } @@ -423,12 +487,20 @@ declare namespace ts.server.protocol { body?: ReferencesResponseBody; } + /** + * Argument for RenameRequest request. + */ export interface RenameRequestArgs extends FileLocationRequestArgs { + /** + * Should text at specified location be found/changed in comments? + */ findInComments?: boolean; + /** + * Should text at specified location be found/changed in strings? + */ findInStrings?: boolean; } - /** * Rename request; value of command field is "rename". Return * response giving the file locations that reference the symbol @@ -503,17 +575,53 @@ declare namespace ts.server.protocol { body?: RenameResponseBody; } + /** + * Represents a file in external project. + * External project is project whose set of files, compilation options and open\close state + * is maintained by the client (i.e. if all this data come from .csproj file in Visual Studio). + * External project will exist even if all files in it are closed and should be closed explicity. + * If external project includes one or more tsconfig.json/jsconfig.json files then tsserver will + * create configured project for every config file but will maintain a link that these projects were created + * as a result of opening external project so they should be removed once external project is closed. + */ export interface ExternalFile { + /** + * Name of file file + */ fileName: string; + /** + * Script kind of the file + */ scriptKind?: ScriptKind; + /** + * Whether file has mixed content (i.e. .cshtml file that combines html markup with C#/JavaScript) + */ hasMixedContent?: boolean; + /** + * Content of the file + */ content?: string; } + /** + * Represent an external project + */ export interface ExternalProject { + /** + * Project name + */ projectFileName: string; + /** + * List of root files in project + */ rootFiles: ExternalFile[]; + /** + * Compiler options for the project + */ options: ExternalProjectCompilerOptions; + /** + * Explicitly specified typing options for the project + */ typingOptions?: TypingOptions; } @@ -522,18 +630,45 @@ declare namespace ts.server.protocol { * compiler settings. */ export interface ExternalProjectCompilerOptions extends CompilerOptions { + /** + * If compile on save is enabled for the project + */ compileOnSave?: boolean; } + /** + * Contains information about current project version + */ export interface ProjectVersionInfo { + /** + * Project name + */ projectName: string; + /** + * true if project is inferred or false if project is external or configured + */ isInferred: boolean; + /** + * Project version + */ version: number; + /** + * Current set of compiler options for project + */ options: CompilerOptions; } + /** + * Represents a set of changes that happen in project + */ export interface ProjectChanges { + /** + * List of added files + */ added: string[]; + /** + * List of removed files + */ removed: string[]; } @@ -545,17 +680,41 @@ declare namespace ts.server.protocol { * otherwise - assume that nothing is changed */ export interface ProjectFiles { + /** + * Information abount project verison + */ info?: ProjectVersionInfo; + /** + * List of files in project (might be omitted if current state of project can be computed using only information from 'changes') + */ files?: string[]; + /** + * Set of changes in project (omitted if the entire set of files in project should be replaced) + */ changes?: ProjectChanges; } + /** + * Combines project information with project level errors. + */ export interface ProjectFilesWithDiagnostics extends ProjectFiles { + /** + * List of errors in project + */ projectErrors: DiagnosticWithLinePosition[]; } + /** + * Represents set of changes in open file + */ export interface ChangedOpenFile { + /** + * Name of file + */ fileName: string; + /** + * List of changes that should be applied to known open file + */ changes: ts.TextChange[]; } @@ -689,54 +848,117 @@ declare namespace ts.server.protocol { arguments: OpenRequestArgs; } - type OpenExternalProjectArgs = ExternalProject; - + /** + * Request to open or update external project + */ export interface OpenExternalProjectRequest extends Request { arguments: OpenExternalProjectArgs; } - export interface CloseExternalProjectRequestArgs { - projectFileName: string; - } + /** + * Arguments to OpenExternalProjectRequest request + */ + type OpenExternalProjectArgs = ExternalProject; + /** + * Request to open multiple external projects + */ export interface OpenExternalProjectsRequest extends Request { arguments: OpenExternalProjectsArgs; } + /** + * Arguments to OpenExternalProjectsRequest + */ export interface OpenExternalProjectsArgs { + /** + * List of external projects to open or update + */ projects: ExternalProject[]; } + /** + * Request to close external project. + */ export interface CloseExternalProjectRequest extends Request { arguments: CloseExternalProjectRequestArgs; } + /** + * Arguments to CloseExternalProjectRequest request + */ + export interface CloseExternalProjectRequestArgs { + /** + * Name of the project to close + */ + projectFileName: string; + } + + /** + * Request to check if given list of projects is up-to-date and synchronize them if necessary + */ export interface SynchronizeProjectListRequest extends Request { arguments: SynchronizeProjectListRequestArgs; } + /** + * Arguments to SynchronizeProjectListRequest + */ export interface SynchronizeProjectListRequestArgs { + /** + * List of last known projects + */ knownProjects: protocol.ProjectVersionInfo[]; } + /** + * Request to synchronize list of open files with the client + */ export interface ApplyChangedToOpenFilesRequest extends Request { arguments: ApplyChangedToOpenFilesRequestArgs; } + /** + * Arguments to ApplyChangedToOpenFilesRequest + */ export interface ApplyChangedToOpenFilesRequestArgs { + /** + * List of newly open files + */ openFiles?: ExternalFile[]; + /** + * List of open files files that were changes + */ changedFiles?: ChangedOpenFile[]; + /** + * List of files that were closed + */ closedFiles?: string[]; } - export interface SetCompilerOptionsForInferredProjectsArgs { - options: ExternalProjectCompilerOptions; - } - + /** + * Request to set compiler options for inferred projects. + * External projects are opened / closed explicitly. + * Configured projects are opened when user opens loose file that has 'tsconfig.json' or 'jsconfig.json' anywhere in one of containing folders. + * This configuration file will be used to obtain a list of files and configuration settings for the project. + * Inferred projects are created when user opens a loose file that is not the part of external project + * or configured project and will contain only open file and transitive closure of referenced files if 'useOneInferredProject' is false, + * or all open loose files and its transitive closure of referenced files if 'useOneInferredProject' is true. + */ export interface SetCompilerOptionsForInferredProjectsRequest extends Request { arguments: SetCompilerOptionsForInferredProjectsArgs; } + /** + * Argument for SetCompilerOptionsForInferredProjectsRequest request. + */ + export interface SetCompilerOptionsForInferredProjectsArgs { + /** + * Compiler options to be used with inferred projects. + */ + options: ExternalProjectCompilerOptions; + } + /** * Exit request; value of command field is "exit". Ask the server process * to exit. @@ -754,23 +976,48 @@ declare namespace ts.server.protocol { export interface CloseRequest extends FileRequest { } + /** + * Request to obtain the list of files that should be regenerated if target file is recompiled. + * NOTE: this us query-only operation and does not generate any output on disk. + */ export interface CompileOnSaveAffectedFileListRequest extends FileRequest { } + /** + * Contains a list of files that should be regenerated in a project + */ export interface CompileOnSaveAffectedFileListSingleProject { + /** + * Project name + */ projectFileName: string; + /** + * List of files names that should be recompiled + */ fileNames: string[]; } + /** + * Response for CompileOnSaveAffectedFileListRequest request; + */ export interface CompileOnSaveAffectedFileListResponse extends Response { body: CompileOnSaveAffectedFileListSingleProject[]; } + /** + * Request to recompile the file. All generated outputs (.js, .d.ts or .js.map files) is written on disk. + */ export interface CompileOnSaveEmitFileRequest extends FileRequest { args: CompileOnSaveEmitFileRequestArgs; } + /** + * Arguments for CompileOnSaveEmitFileRequest + */ export interface CompileOnSaveEmitFileRequestArgs extends FileRequestArgs { + /** + * if true - then file should be recompiled even if it does not have any changes. + */ forced?: boolean; } @@ -839,7 +1086,14 @@ declare namespace ts.server.protocol { */ endOffset: number; + /** + * End position of the range for which to format text in file. + */ endPosition?: number; + + /** + * Format options to be used. + */ options?: ts.FormatCodeOptions; } diff --git a/src/server/session.ts b/src/server/session.ts index edb79e3a0ef..82ca7be0d16 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -342,7 +342,7 @@ namespace ts.server { } } - private getEncodedSemanticClassifications(args: protocol.FileSpanRequestArgs) { + private getEncodedSemanticClassifications(args: protocol.SemanticDiagnosticsRequestArgs) { const { file, project } = this.getFileAndProject(args); return project.getLanguageService().getEncodedSemanticClassifications(file, args); } @@ -351,7 +351,7 @@ namespace ts.server { return projectFileName && this.projectService.findProject(projectFileName); } - private getCompilerOptionsDiagnostics(args: protocol.ProjectRequestArgs) { + private getCompilerOptionsDiagnostics(args: protocol.CompilerOptionsDiagnosticsRequestArgs) { const project = this.getProject(args.projectFileName); return this.convertToDiagnosticsWithLinePosition(project.getLanguageService().getCompilerOptionsDiagnostics(), /*scriptInfo*/ undefined); } @@ -1438,10 +1438,10 @@ namespace ts.server { [CommandNames.SignatureHelpFull]: (request: protocol.SignatureHelpRequest) => { return this.requiredResponse(this.getSignatureHelpItems(request.arguments, /*simplifiedResult*/ false)); }, - [CommandNames.CompilerOptionsDiagnosticsFull]: (request: protocol.ProjectRequest) => { + [CommandNames.CompilerOptionsDiagnosticsFull]: (request: protocol.CompilerOptionsDiagnosticsRequest) => { return this.requiredResponse(this.getCompilerOptionsDiagnostics(request.arguments)); }, - [CommandNames.EncodedSemanticClassificationsFull]: (request: protocol.FileSpanRequest) => { + [CommandNames.EncodedSemanticClassificationsFull]: (request: protocol.SemanticDiagnosticsRequest) => { return this.requiredResponse(this.getEncodedSemanticClassifications(request.arguments)); }, [CommandNames.Cleanup]: (request: protocol.Request) => { From 3b0515fd8b8742d1c25c70642e853a9ed4e324c7 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Mon, 10 Oct 2016 16:38:08 -0700 Subject: [PATCH 4/6] Send config file diagnonstics event even when no errors were found (#11285) * emit config diagnostics event when no errors found * Add tests for project service events --- .../unittests/tsserverProjectSystem.ts | 63 ++++++++++++++++++- src/server/editorServices.ts | 12 ++-- src/server/session.ts | 20 +++--- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 9f0d6ae6a17..f2decc1c339 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts.projectSystem { @@ -136,6 +136,19 @@ namespace ts.projectSystem { return map(fileNames, toExternalFile); } + export class TestServerEventManager { + private events: server.ProjectServiceEvent[] = []; + + handler: server.ProjectServiceEventHandler = (event: server.ProjectServiceEvent) => { + this.events.push(event); + } + + checkEventCountOfType(eventType: "context" | "configFileDiag", expectedCount: number) { + const eventsOfType = filter(this.events, e => e.eventName === eventType); + assert.equal(eventsOfType.length, expectedCount, `The actual event counts of type ${eventType} is ${eventsOfType.length}, while expected ${expectedCount}`); + } + } + export interface TestServerHostCreationParameters { useCaseSensitiveFileNames?: boolean; executingFilePath?: string; @@ -159,11 +172,11 @@ namespace ts.projectSystem { return host; } - export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller) { + export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler) { if (typingsInstaller === undefined) { typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host); } - return new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); + return new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ projectServiceEventHandler !== undefined, projectServiceEventHandler); } export interface CreateProjectServiceParameters { @@ -2121,4 +2134,48 @@ namespace ts.projectSystem { projectService.inferredProjects[0].getLanguageService().getProgram(); }); }); + + describe("Configure file diagnostics events", () => { + + it("are generated when the config file has errors", () => { + const serverEventManager = new TestServerEventManager(); + const file = { + path: "/a/b/app.ts", + content: "let x = 10" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": { + "foo": "bar", + "allowJS": true + } + }` + }; + + const host = createServerHost([file, configFile]); + const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler); + openFilesForSession([file], session); + serverEventManager.checkEventCountOfType("configFileDiag", 1); + }); + + it("are generated when the config file doesn't have errors", () => { + const serverEventManager = new TestServerEventManager(); + const file = { + path: "/a/b/app.ts", + content: "let x = 10" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {} + }` + }; + + const host = createServerHost([file, configFile]); + const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler); + openFilesForSession([file], session); + serverEventManager.checkEventCountOfType("configFileDiag", 1); + }); + }); } \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 51310c90a6b..50dd5612da8 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -755,12 +755,14 @@ namespace ts.server { } private reportConfigFileDiagnostics(configFileName: string, diagnostics: Diagnostic[], triggerFile?: string) { - if (diagnostics && diagnostics.length > 0) { - this.eventHandler({ - eventName: "configFileDiag", - data: { configFileName, diagnostics, triggerFile } - }); + if (!this.eventHandler) { + return; } + + this.eventHandler({ + eventName: "configFileDiag", + data: { configFileName, diagnostics: diagnostics || [], triggerFile } + }); } private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: Diagnostic[], clientFileName?: string) { diff --git a/src/server/session.ts b/src/server/session.ts index 82ca7be0d16..3b95bdd6fb9 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -155,6 +155,8 @@ namespace ts.server { private immediateId: any; private changeSeq = 0; + private eventHander: ProjectServiceEventHandler; + constructor( private host: ServerHost, cancellationToken: HostCancellationToken, @@ -163,17 +165,18 @@ namespace ts.server { private byteLength: (buf: string, encoding?: string) => number, private hrtime: (start?: number[]) => number[], protected logger: Logger, - protected readonly canUseEvents: boolean) { + protected readonly canUseEvents: boolean, + eventHandler?: ProjectServiceEventHandler) { - const eventHandler: ProjectServiceEventHandler = canUseEvents - ? event => this.handleEvent(event) + this.eventHander = canUseEvents + ? eventHandler || (event => this.defaultEventHandler(event)) : undefined; - this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler); + this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander); this.gcTimer = new GcTimer(host, /*delay*/ 7000, logger); } - private handleEvent(event: ProjectServiceEvent) { + private defaultEventHandler(event: ProjectServiceEvent) { switch (event.eventName) { case "context": const { project, fileName } = event.data; @@ -734,8 +737,11 @@ namespace ts.server { */ private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind) { const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind); - if (configFileErrors) { - this.configFileDiagnosticEvent(fileName, configFileName, configFileErrors); + if (this.eventHander) { + this.eventHander({ + eventName: "configFileDiag", + data: { fileName, configFileName, diagnostics: configFileErrors || [] } + }); } } From 413a26b94d4cea5d0b1b3aa08ee149794437df77 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 11 Oct 2016 11:14:15 -0700 Subject: [PATCH 5/6] Support JSDoc for type alias --- src/compiler/parser.ts | 2 +- tests/cases/fourslash/jsDocForTypeAlias.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/jsDocForTypeAlias.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index aaf3b4ebecc..62ae61e4cb4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5352,7 +5352,7 @@ namespace ts { parseExpected(SyntaxKind.EqualsToken); node.type = parseType(); parseSemicolon(); - return finishNode(node); + return addJSDocComment(finishNode(node)); } // In an ambient declaration, the grammar only allows integer literals as initializers. diff --git a/tests/cases/fourslash/jsDocForTypeAlias.ts b/tests/cases/fourslash/jsDocForTypeAlias.ts new file mode 100644 index 00000000000..dbda7ccbc13 --- /dev/null +++ b/tests/cases/fourslash/jsDocForTypeAlias.ts @@ -0,0 +1,7 @@ +/// + +/////** DOC */ +////type /**/T = number + +goTo.marker(); +verify.quickInfoIs("type T = number", "DOC "); From 2671668d8637d8984b8f861e1234ab06e70730e8 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Tue, 11 Oct 2016 13:41:04 -0700 Subject: [PATCH 6/6] Remove cached resolvedModule result if the target file was deleted (#11460) * Add test first for debugging * Remove cached resolvedModule if the file was deleted * Move the fix to lsHost * Properly clean lastDeletedFile * Remove unused builder API * move the delete file check into module name valid check --- .../unittests/tsserverProjectSystem.ts | 133 +++++++++++++++++- src/server/editorServices.ts | 6 +- src/server/lsHost.ts | 10 +- 3 files changed, 144 insertions(+), 5 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index f2decc1c339..b758761ee76 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -2097,7 +2097,7 @@ namespace ts.projectSystem { const projectFileName = "externalProject"; const host = createServerHost([f]); const projectService = createProjectService(host); - // create a project + // create a project projectService.openExternalProject({ projectFileName, rootFiles: [toExternalFile(f.path)], options: {} }); projectService.checkNumberOfProjects({ externalProjects: 1 }); @@ -2135,6 +2135,137 @@ namespace ts.projectSystem { }); }); + describe("rename a module file and rename back", () => { + it("should restore the states for inferred projects", () => { + const moduleFile = { + path: "/a/b/moduleFile.ts", + content: "export function bar() { };" + }; + const file1 = { + path: "/a/b/file1.ts", + content: "import * as T from './moduleFile'; T.bar();" + }; + const host = createServerHost([moduleFile, file1]); + const session = createSession(host); + + openFilesForSession([file1], session); + const getErrRequest = makeSessionRequest( + server.CommandNames.SemanticDiagnosticsSync, + { file: file1.path } + ); + let diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 0); + + const moduleFileOldPath = moduleFile.path; + const moduleFileNewPath = "/a/b/moduleFile1.ts"; + moduleFile.path = moduleFileNewPath; + host.reloadFS([moduleFile, file1]); + host.triggerFileWatcherCallback(moduleFileOldPath); + host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path); + host.runQueuedTimeoutCallbacks(); + diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 1); + + moduleFile.path = moduleFileOldPath; + host.reloadFS([moduleFile, file1]); + host.triggerFileWatcherCallback(moduleFileNewPath); + host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path); + host.runQueuedTimeoutCallbacks(); + + // Make a change to trigger the program rebuild + const changeRequest = makeSessionRequest( + server.CommandNames.Change, + { file: file1.path, line: 1, offset: 44, endLine: 1, endOffset: 44, insertString: "\n" } + ); + session.executeCommand(changeRequest); + host.runQueuedTimeoutCallbacks(); + + diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 0); + }); + + it("should restore the states for configured projects", () => { + const moduleFile = { + path: "/a/b/moduleFile.ts", + content: "export function bar() { };" + }; + const file1 = { + path: "/a/b/file1.ts", + content: "import * as T from './moduleFile'; T.bar();" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = createServerHost([moduleFile, file1, configFile]); + const session = createSession(host); + + openFilesForSession([file1], session); + const getErrRequest = makeSessionRequest( + server.CommandNames.SemanticDiagnosticsSync, + { file: file1.path } + ); + let diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 0); + + const moduleFileOldPath = moduleFile.path; + const moduleFileNewPath = "/a/b/moduleFile1.ts"; + moduleFile.path = moduleFileNewPath; + host.reloadFS([moduleFile, file1, configFile]); + host.triggerFileWatcherCallback(moduleFileOldPath); + host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path); + host.runQueuedTimeoutCallbacks(); + diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 1); + + moduleFile.path = moduleFileOldPath; + host.reloadFS([moduleFile, file1, configFile]); + host.triggerFileWatcherCallback(moduleFileNewPath); + host.triggerDirectoryWatcherCallback("/a/b", moduleFile.path); + host.runQueuedTimeoutCallbacks(); + diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 0); + }); + + }); + + describe("add the missing module file for inferred project", () => { + it("should remove the `module not found` error", () => { + const moduleFile = { + path: "/a/b/moduleFile.ts", + content: "export function bar() { };" + }; + const file1 = { + path: "/a/b/file1.ts", + content: "import * as T from './moduleFile'; T.bar();" + }; + const host = createServerHost([file1]); + const session = createSession(host); + openFilesForSession([file1], session); + const getErrRequest = makeSessionRequest( + server.CommandNames.SemanticDiagnosticsSync, + { file: file1.path } + ); + let diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 1); + + host.reloadFS([file1, moduleFile]); + host.triggerDirectoryWatcherCallback(getDirectoryPath(file1.path), moduleFile.path); + host.runQueuedTimeoutCallbacks(); + + // Make a change to trigger the program rebuild + const changeRequest = makeSessionRequest( + server.CommandNames.Change, + { file: file1.path, line: 1, offset: 44, endLine: 1, endOffset: 44, insertString: "\n" } + ); + session.executeCommand(changeRequest); + + // Recheck + diags = session.executeCommand(getErrRequest).response; + assert.equal(diags.length, 0); + }); + }); + describe("Configure file diagnostics events", () => { it("are generated when the config file has errors", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 50dd5612da8..dc2c45156cd 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -180,6 +180,8 @@ namespace ts.server { private toCanonicalFileName: (f: string) => string; + public lastDeletedFile: ScriptInfo; + constructor(public readonly host: ServerHost, public readonly logger: Logger, public readonly cancellationToken: HostCancellationToken, @@ -272,7 +274,7 @@ namespace ts.server { else { projectsToUpdate = []; for (const f of this.changedFiles) { - projectsToUpdate = projectsToUpdate.concat(f.containingProjects); + projectsToUpdate = projectsToUpdate.concat(f.containingProjects); } } this.updateProjectGraphs(projectsToUpdate); @@ -342,6 +344,7 @@ namespace ts.server { if (!info.isOpen) { this.filenameToScriptInfo.remove(info.path); + this.lastDeletedFile = info; // capture list of projects since detachAllProjects will wipe out original list const containingProjects = info.containingProjects.slice(); @@ -350,6 +353,7 @@ namespace ts.server { // update projects to make sure that set of referenced files is correct this.updateProjectGraphs(containingProjects); + this.lastDeletedFile = undefined; if (!this.eventHandler) { return; diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index dedbb14c929..b36f73a19c5 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -52,7 +52,7 @@ namespace ts.server { }; } - private resolveNamesWithLocalCache( + private resolveNamesWithLocalCache( names: string[], containingFile: string, cache: ts.FileMap>, @@ -65,6 +65,7 @@ namespace ts.server { const newResolutions: Map = createMap(); const resolvedModules: R[] = []; const compilerOptions = this.getCompilationSettings(); + const lastDeletedFileName = this.project.projectService.lastDeletedFile && this.project.projectService.lastDeletedFile.fileName; for (const name of names) { // check if this is a duplicate entry in the list @@ -94,8 +95,11 @@ namespace ts.server { return false; } - if (getResult(resolution)) { - // TODO: consider checking failedLookupLocations + const result = getResult(resolution); + if (result) { + if (result.resolvedFileName && result.resolvedFileName === lastDeletedFileName) { + return false; + } return true; }