diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 181f84b4c15..af0697a12d9 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -2006,6 +2006,7 @@ namespace ts { * @param host The host used to resolve files and directories. * @param extraFileExtensions optionaly file extra file extension information from host */ + /* @internal */ export function getFileNamesFromConfigSpecs(spec: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: ReadonlyArray = []): ExpandResult { basePath = normalizePath(basePath); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 8d5883b7755..c912cfa6b53 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -866,6 +866,11 @@ namespace ts { return elementAt(array, 0); } + export function first(array: ReadonlyArray): T { + Debug.assert(array.length !== 0); + return array[0]; + } + /** * Returns the last element of an array if non-empty, `undefined` otherwise. */ @@ -873,6 +878,11 @@ namespace ts { return elementAt(array, -1); } + export function last(array: ReadonlyArray): T { + Debug.assert(array.length !== 0); + return array[array.length - 1]; + } + /** * Returns the only element of an array if it contains only one element, `undefined` otherwise. */ diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 87e14f8ae67..bb7de450db6 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2180,7 +2180,7 @@ namespace ts { function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType | SyntaxKind.JSDocNonNullableType): TypeNode { const result = createNode(kind) as JSDocVariadicType | JSDocNonNullableType; nextToken(); - result.type = parseType(); + result.type = parseNonArrayType(); return finishNode(result); } @@ -2747,49 +2747,53 @@ namespace ts { return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType(); } - function parseJSDocPostfixTypeOrHigher(): TypeNode { - const type = parseNonArrayType(); - const kind = getKind(token()); - if (!kind) return type; - nextToken(); - - const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType; - postfix.type = type; - return finishNode(postfix); - - function getKind(tokenKind: SyntaxKind): SyntaxKind | undefined { - switch (tokenKind) { + function parsePostfixTypeOrHigher(): TypeNode { + let type = parseNonArrayType(); + while (!scanner.hasPrecedingLineBreak()) { + switch (token()) { case SyntaxKind.EqualsToken: // only parse postfix = inside jsdoc, because it's ambiguous elsewhere - return contextFlags & NodeFlags.JSDoc ? SyntaxKind.JSDocOptionalType : undefined; + if (!(contextFlags & NodeFlags.JSDoc)) { + return type; + } + type = createJSDocPostfixType(SyntaxKind.JSDocOptionalType, type); + break; case SyntaxKind.ExclamationToken: - return SyntaxKind.JSDocNonNullableType; + type = createJSDocPostfixType(SyntaxKind.JSDocNonNullableType, type); + break; case SyntaxKind.QuestionToken: - return SyntaxKind.JSDocNullableType; - } - } - } - - function parseArrayTypeOrHigher(): TypeNode { - let type = parseJSDocPostfixTypeOrHigher(); - while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) { - if (isStartOfType()) { - const node = createNode(SyntaxKind.IndexedAccessType, type.pos); - node.objectType = type; - node.indexType = parseType(); - parseExpected(SyntaxKind.CloseBracketToken); - type = finishNode(node); - } - else { - const node = createNode(SyntaxKind.ArrayType, type.pos); - node.elementType = type; - parseExpected(SyntaxKind.CloseBracketToken); - type = finishNode(node); + type = createJSDocPostfixType(SyntaxKind.JSDocNullableType, type); + break; + case SyntaxKind.OpenBracketToken: + parseExpected(SyntaxKind.OpenBracketToken); + if (isStartOfType()) { + const node = createNode(SyntaxKind.IndexedAccessType, type.pos) as IndexedAccessTypeNode; + node.objectType = type; + node.indexType = parseType(); + parseExpected(SyntaxKind.CloseBracketToken); + type = finishNode(node); + } + else { + const node = createNode(SyntaxKind.ArrayType, type.pos) as ArrayTypeNode; + node.elementType = type; + parseExpected(SyntaxKind.CloseBracketToken); + type = finishNode(node); + } + break; + default: + return type; } } return type; } + function createJSDocPostfixType(kind: SyntaxKind, type: TypeNode) { + nextToken(); + const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType; + postfix.type = type; + return finishNode(postfix); + } + function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword) { const node = createNode(SyntaxKind.TypeOperator); parseExpected(operator); @@ -2803,7 +2807,7 @@ namespace ts { case SyntaxKind.KeyOfKeyword: return parseTypeOperator(SyntaxKind.KeyOfKeyword); } - return parseArrayTypeOrHigher(); + return parsePostfixTypeOrHigher(); } function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 36bb0a73729..24d7707f50d 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -403,6 +403,7 @@ namespace ts { /** * Determines if program structure is upto date or needs to be recreated */ + /* @internal */ export function isProgramUptoDate( program: Program | undefined, rootFileNames: string[], diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 03bbd874779..a0637f57a3b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2532,7 +2532,7 @@ namespace ts { /** Set of all source files that some other source file redirects to. */ /* @internal */ redirectTargetsSet: Map; /** Returns true when file in the program had invalidated resolution at the time of program creation. */ - hasInvalidatedResolution: HasInvalidatedResolution; + /* @internal */ hasInvalidatedResolution: HasInvalidatedResolution; } /* @internal */ @@ -3853,7 +3853,7 @@ namespace ts { errors: Diagnostic[]; wildcardDirectories?: MapLike; compileOnSave?: boolean; - configFileSpecs?: ConfigFileSpecs; + /* @internal */ configFileSpecs?: ConfigFileSpecs; } export const enum WatchDirectoryFlags { @@ -3861,6 +3861,7 @@ namespace ts { Recursive = 1 << 0, } + /* @internal */ export interface ConfigFileSpecs { filesSpecs: ReadonlyArray; /** @@ -3879,7 +3880,7 @@ namespace ts { export interface ExpandResult { fileNames: string[]; wildcardDirectories: MapLike; - spec: ConfigFileSpecs; + /* @internal */ spec: ConfigFileSpecs; } /* @internal */ @@ -4151,6 +4152,7 @@ namespace ts { readonly failedLookupLocations: ReadonlyArray; } + /* @internal */ export interface HasInvalidatedResolution { (sourceFile: Path): boolean; } @@ -4181,9 +4183,9 @@ namespace ts { */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; getEnvironmentVariable?(name: string): string; - onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void; - hasInvalidatedResolution?: HasInvalidatedResolution; - hasChangedAutomaticTypeDirectiveNames?: boolean; + /* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void; + /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; + /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; } /* @internal */ diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 5ca6efbd57d..00cb09412bd 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -37,13 +37,13 @@ namespace ts.refactor.extractSymbol { const usedConstantNames: Map = createMap(); let i = 0; - for (const extraction of extractions) { + for (const {functionExtraction, constantExtraction} of extractions) { // Skip these since we don't have a way to report errors yet - if (extraction.functionErrors.length === 0) { + if (functionExtraction.errors.length === 0) { // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will // preferentially go into nearer scopes - const description = formatStringFromArgs(Diagnostics.Extract_to_0_in_1.message, [extraction.functionDescription, extraction.functionScopeDescription]); + const description = formatStringFromArgs(Diagnostics.Extract_to_0_in_1.message, [functionExtraction.description, functionExtraction.scopeDescription]); if (!usedFunctionNames.has(description)) { usedFunctionNames.set(description, true); functionActions.push({ @@ -54,11 +54,11 @@ namespace ts.refactor.extractSymbol { } // Skip these since we don't have a way to report errors yet - if (extraction.constantErrors.length === 0) { + if (constantExtraction.errors.length === 0) { // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will // preferentially go into nearer scopes - const description = formatStringFromArgs(Diagnostics.Extract_to_0_in_1.message, [extraction.constantDescription, extraction.constantScopeDescription]); + const description = formatStringFromArgs(Diagnostics.Extract_to_0_in_1.message, [constantExtraction.description, constantExtraction.scopeDescription]); if (!usedConstantNames.has(description)) { usedConstantNames.set(description, true); constantActions.push({ @@ -469,7 +469,7 @@ namespace ts.refactor.extractSymbol { * depending on what's in the extracted body. */ function collectEnclosingScopes(range: TargetRange): Scope[] | undefined { - let current: Node = isReadonlyArray(range.range) ? firstOrUndefined(range.range) : range.range; + let current: Node = isReadonlyArray(range.range) ? first(range.range) : range.range; if (range.facts & RangeFacts.UsesThis) { // if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class const containingClass = getContainingClass(current); @@ -521,37 +521,44 @@ namespace ts.refactor.extractSymbol { return extractConstantInScope(expression, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange.facts, context); } - interface PossibleExtraction { - readonly functionDescription: string; - readonly functionScopeDescription: string; - readonly functionErrors: ReadonlyArray; - readonly constantDescription: string; - readonly constantScopeDescription: string; - readonly constantErrors: ReadonlyArray; + interface Extraction { + readonly description: string; + readonly scopeDescription: string; + readonly errors: ReadonlyArray; } + + interface ScopeExtractions { + readonly functionExtraction: Extraction; + readonly constantExtraction: Extraction; + } + /** * Given a piece of text to extract ('targetRange'), computes a list of possible extractions. * Each returned ExtractResultForScope corresponds to a possible target scope and is either a set of changes * or an error explaining why we can't extract into that scope. */ - function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray | undefined { + function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray | undefined { const { scopes, readsAndWrites: { functionErrorsPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); // Need the inner type annotation to avoid https://github.com/Microsoft/TypeScript/issues/7547 - const extractions = scopes.map((scope, i): PossibleExtraction => { + const extractions = scopes.map((scope, i): ScopeExtractions => { const scopeDescription = isFunctionLikeDeclaration(scope) ? getDescriptionForFunctionLikeDeclaration(scope) : isClassLike(scope) ? getDescriptionForClassLikeDeclaration(scope) : getDescriptionForModuleLikeDeclaration(scope); return { - functionDescription: getDescriptionForFunctionInScope(scope), - functionErrors: functionErrorsPerScope[i], - functionScopeDescription: scopeDescription, - constantDescription: getDescriptionForConstantInScope(scope), - constantErrors: constantErrorsPerScope[i], - constantScopeDescription: (i === 0 && !isClassLike(scope)) - ? "enclosing scope" // Like "global scope" and "module scope", this is not localized. - : scopeDescription, + functionExtraction: { + description: getDescriptionForFunctionInScope(scope), + errors: functionErrorsPerScope[i], + scopeDescription, + }, + constantExtraction: { + description: getDescriptionForConstantInScope(scope), + errors: constantErrorsPerScope[i], + scopeDescription: (i === 0 && !isClassLike(scope)) + ? "enclosing scope" // Like "global scope" and "module scope", this is not localized. + : scopeDescription, + }, }; }); return extractions; @@ -739,7 +746,7 @@ namespace ts.refactor.extractSymbol { } const changeTracker = textChanges.ChangeTracker.fromContext(context); - const minInsertionPos = (isReadonlyArray(range.range) ? lastOrUndefined(range.range) : range.range).end; + const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end; const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, { suffix: context.newLineCharacter + context.newLineCharacter }); @@ -823,7 +830,7 @@ namespace ts.refactor.extractSymbol { } const edits = changeTracker.getChanges(); - const renameRange = isReadonlyArray(range.range) ? range.range[0] : range.range; + const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range; const renameFilename = renameRange.getSourceFile().fileName; const renameLocation = getRenameLocation(edits, renameFilename, functionNameText, /*isDeclaredBeforeUse*/ false); @@ -1219,7 +1226,7 @@ namespace ts.refactor.extractSymbol { */ function getEnclosingTextRange(targetRange: TargetRange, sourceFile: SourceFile): TextRange { return isReadonlyArray(targetRange.range) - ? { pos: targetRange.range[0].getStart(sourceFile), end: targetRange.range[targetRange.range.length - 1].getEnd() } + ? { pos: first(targetRange.range).getStart(sourceFile), end: last(targetRange.range).getEnd() } : targetRange.range; } @@ -1265,7 +1272,7 @@ namespace ts.refactor.extractSymbol { const expressionDiagnostic = isReadonlyArray(targetRange.range) && !(targetRange.range.length === 1 && isExpressionStatement(targetRange.range[0])) - ? ((start, end) => createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected))(firstOrUndefined(targetRange.range).getStart(), lastOrUndefined(targetRange.range).end) + ? ((start, end) => createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected))(first(targetRange.range).getStart(), last(targetRange.range).end) : undefined; // initialize results @@ -1292,7 +1299,7 @@ namespace ts.refactor.extractSymbol { const target = isReadonlyArray(targetRange.range) ? createBlock(targetRange.range) : targetRange.range; const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent) ? scopes[0] : getEnclosingBlockScopeContainer(scopes[0]); - const unmodifiedNode = isReadonlyArray(targetRange.range) ? targetRange.range[0] : targetRange.range; + const unmodifiedNode = isReadonlyArray(targetRange.range) ? first(targetRange.range) : targetRange.range; const inGenericContext = isInGenericContext(unmodifiedNode); collectUsages(target); diff --git a/src/services/types.ts b/src/services/types.ts index 4c77cd14d9c..e853eb7b96c 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -185,8 +185,8 @@ namespace ts { */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; - hasInvalidatedResolution?: HasInvalidatedResolution; - hasChangedAutomaticTypeDirectiveNames?: boolean; + /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; + /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; directoryExists?(directoryName: string): boolean; /* diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 96303f6b109..7adc7539ca6 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2405,22 +2405,23 @@ declare namespace ts { Dts = ".d.ts", Js = ".js", Jsx = ".jsx", + Json = ".json", } interface ResolvedModuleWithFailedLookupLocations { - resolvedModule: ResolvedModuleFull | undefined; + readonly resolvedModule: ResolvedModuleFull | undefined; } interface ResolvedTypeReferenceDirective { primary: boolean; - resolvedFileName?: string; + resolvedFileName: string | undefined; packageId?: PackageId; } interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { - resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; - failedLookupLocations: string[]; + readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; + readonly failedLookupLocations: ReadonlyArray; } interface CompilerHost extends ModuleResolutionHost { - getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined; - getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined; + getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; + getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getCancellationToken?(): CancellationToken; getDefaultLibFileName(options: CompilerOptions): string; getDefaultLibLocation?(): string; @@ -2430,7 +2431,7 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ @@ -2683,14 +2684,26 @@ declare namespace ts { callback: FileWatcherCallback; mtime?: Date; } - interface System { - args: string[]; + /** + * Partial interface of the System thats needed to support the caching of directory structure + */ + interface DirectoryStructureHost { newLine: string; useCaseSensitiveFileNames: boolean; write(s: string): void; readFile(path: string, encoding?: string): string | undefined; - getFileSize?(path: string): number; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + exit(exitCode?: number): void; + } + interface System extends DirectoryStructureHost { + args: string[]; + getFileSize?(path: string): number; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching @@ -2698,13 +2711,7 @@ declare namespace ts { watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; getExecutingFilePath(): string; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -2712,7 +2719,6 @@ declare namespace ts { */ createHash?(data: string): string; getMemoryUsage?(): number; - exit(exitCode?: number): void; realpath?(path: string): string; setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout?(timeoutId: any): void; @@ -2720,10 +2726,6 @@ declare namespace ts { interface FileWatcher { close(): void; } - interface DirectoryWatcher extends FileWatcher { - directoryName: string; - referenceCount: number; - } function getNodeMajorVersion(): number; let sys: System; } @@ -3710,6 +3712,23 @@ declare namespace ts { declare namespace ts { function createPrinter(printerOptions?: PrinterOptions, handlers?: PrintHandlers): Printer; } +declare namespace ts { + interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + interface EmitOutputDetailed extends EmitOutput { + diagnostics: Diagnostic[]; + sourceMaps: SourceMapData[]; + emittedSourceFiles: SourceFile[]; + } + interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } + function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed; +} declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; function resolveTripleslashReference(moduleName: string, containingFile: string): string; @@ -3721,6 +3740,7 @@ declare namespace ts { getNewLine(): string; } function formatDiagnostics(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; + function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; /** @@ -3856,7 +3876,7 @@ declare namespace ts { readFile?(path: string, encoding?: string): string | undefined; fileExists?(path: string): boolean; getTypeRootsVersion?(): number; - resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; directoryExists?(directoryName: string): boolean; getDirectories?(directoryName: string): string[]; @@ -3914,6 +3934,7 @@ declare namespace ts { getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed; getProgram(): Program; dispose(): void; } @@ -4270,20 +4291,11 @@ declare namespace ts { */ autoCollapse: boolean; } - interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } enum OutputFileType { JavaScript = 0, SourceMap = 1, Declaration = 2, } - interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } enum EndOfLineState { None = 0, InMultiLineCommentTrivia = 1, @@ -4751,28 +4763,6 @@ declare namespace ts.server { function isInferredProjectName(name: string): boolean; function makeInferredProjectName(counter: number): string; function createSortedArray(): SortedArray; - class ThrottledOperations { - private readonly host; - private pendingTimeouts; - constructor(host: ServerHost); - /** - * Wait `number` milliseconds and then invoke `cb`. If, while waiting, schedule - * is called again with the same `operationId`, cancel this operation in favor - * of the new one. (Note that the amount of time the canceled operation had been - * waiting does not affect the amount of time that the new operation waits.) - */ - schedule(operationId: string, delay: number, cb: () => void): void; - private static run(self, operationId, cb); - } - class GcTimer { - private readonly host; - private readonly delay; - private readonly logger; - private timerId; - constructor(host: ServerHost, delay: number, logger: Logger); - scheduleCollect(): void; - private static run(self); - } } /** * Declaration module describing the TypeScript Server protocol @@ -6359,6 +6349,17 @@ declare namespace ts.server.protocol { */ languageServiceEnabled: boolean; } + type ProjectsUpdatedInBackgroundEventName = "projectsUpdatedInBackground"; + interface ProjectsUpdatedInBackgroundEvent extends Event { + event: ProjectsUpdatedInBackgroundEventName; + body: ProjectsUpdatedInBackgroundEventBody; + } + interface ProjectsUpdatedInBackgroundEventBody { + /** + * Current set of open files + */ + openFiles: string[]; + } /** * Arguments for reload request. */ @@ -6822,14 +6823,13 @@ declare namespace ts.server { constructor(opts: SessionOptions); private sendRequestCompletedEvent(requestId); private defaultEventHandler(event); + private projectsUpdatedInBackgroundEvent(openFiles); logError(err: Error, cmd: string): void; send(msg: protocol.Message): void; - configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ReadonlyArray): void; event(info: T, eventName: string): void; output(info: any, cmdName: string, reqSeq?: number, errorMsg?: string): void; private semanticCheck(file, project); private syntacticCheck(file, project); - private updateProjectStructure(); private updateErrorCheck(next, checkList, ms, requireOpen?); private cleanProjects(caption, projects); private cleanup(); @@ -6862,9 +6862,10 @@ declare namespace ts.server { */ private openClientFile(fileName, fileContent?, scriptKind?, projectRootPath?); private getPosition(args, scriptInfo); - private getFileAndProject(args, errorOnMissingProject?); - private getFileAndProjectWithoutRefreshingInferredProjects(args, errorOnMissingProject?); - private getFileAndProjectWorker(uncheckedFileName, projectFileName, refreshInferredProjects, errorOnMissingProject); + private getPositionInFile(args, file); + private getFileAndProject(args); + private getFileAndLanguageServiceForSyntacticOperation(args); + private getFileAndProjectWorker(uncheckedFileName, projectFileName); private getOutliningSpans(args); private getTodoComments(args); private getDocCommentTemplate(args); @@ -6884,6 +6885,7 @@ declare namespace ts.server { private getCompileOnSaveAffectedFileList(args); private emitFile(args); private getSignatureHelpItems(args, simplifiedResult); + private createCheckList(fileNames, defaultProject?); private getDiagnostics(next, delay, fileNames); private change(args); private reload(args, reqSeq); @@ -6926,51 +6928,20 @@ declare namespace ts.server { onMessage(message: string): void; } } -declare namespace ts.server { - interface AbsolutePositionAndLineText { - absolutePosition: number; - lineText: string | undefined; - } - class ScriptVersionCache { - private changes; - private readonly versions; - private minVersion; - private currentVersion; - private static readonly changeNumberThreshold; - private static readonly changeLengthThreshold; - private static readonly maxVersions; - private versionToIndex(version); - private currentVersionToIndex(); - edit(pos: number, deleteLen: number, insertedText?: string): void; - reload(script: string): void; - getSnapshot(): IScriptSnapshot; - private _getSnapshot(); - getSnapshotVersion(): number; - getLineInfo(line: number): AbsolutePositionAndLineText; - lineOffsetToPosition(line: number, column: number): number; - positionToLineOffset(position: number): protocol.Location; - lineToTextSpan(line: number): TextSpan; - getTextChangesBetweenVersions(oldVersion: number, newVersion: number): TextChangeRange; - static fromString(script: string): ScriptVersionCache; - } -} declare namespace ts.server { class ScriptInfo { private readonly host; readonly fileName: NormalizedPath; readonly scriptKind: ScriptKind; - hasMixedContent: boolean; - isDynamic: boolean; + readonly hasMixedContent: boolean; + readonly path: Path; /** * All projects that include this file */ readonly containingProjects: Project[]; private formatCodeSettings; - readonly path: Path; - private fileWatcher; private textStorage; - private isOpen; - constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent?: boolean, isDynamic?: boolean); + constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path); isScriptOpen(): boolean; open(newText: string): void; close(): void; @@ -6983,15 +6954,12 @@ declare namespace ts.server { getDefaultProject(): Project; registerFileUpdate(): void; setFormatOptions(formatSettings: FormatCodeSettings): void; - setWatcher(watcher: FileWatcher): void; - stopWatcher(): void; getLatestVersion(): string; - reload(script: string): void; saveTo(fileName: string): void; reloadFromFile(tempFileName?: NormalizedPath): void; - getLineInfo(line: number): AbsolutePositionAndLineText; editContent(start: number, end: number, newText: string): void; markContainingProjectsAsDirty(): void; + isOrphan(): boolean; /** * @param line 1 based index */ @@ -7005,47 +6973,22 @@ declare namespace ts.server { isJavaScript(): boolean; } } -declare namespace ts.server { - class LSHost implements LanguageServiceHost, ModuleResolutionHost { - private readonly host; - private project; - private readonly cancellationToken; - private compilationSettings; - private readonly resolvedModuleNames; - private readonly resolvedTypeReferenceDirectives; - private readonly getCanonicalFileName; - private filesWithChangedSetOfUnresolvedImports; - private resolveModuleName; - readonly trace: (s: string) => void; - readonly realpath?: (path: string) => string; - constructor(host: ServerHost, project: Project, cancellationToken: HostCancellationToken); - dispose(): void; - startRecordingFilesWithChangedResolutions(): void; - finishRecordingFilesWithChangedResolutions(): Path[]; - private resolveNamesWithLocalCache(names, containingFile, cache, loader, getResult, getResultFileName, logChanges); - getNewLine(): string; - getProjectVersion(): string; - getCompilationSettings(): CompilerOptions; - useCaseSensitiveFileNames(): boolean; - getCancellationToken(): HostCancellationToken; - resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; - resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModuleFull[]; - getDefaultLibFileName(): string; - getScriptSnapshot(filename: string): IScriptSnapshot; - getScriptFileNames(): string[]; - getTypeRootsVersion(): number; - getScriptKind(fileName: string): ScriptKind; - getScriptVersion(filename: string): string; - getCurrentDirectory(): string; - resolvePath(path: string): string; - fileExists(file: string): boolean; - readFile(fileName: string): string | undefined; - directoryExists(path: string): boolean; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; - getDirectories(path: string): string[]; - notifyFileRemoved(info: ScriptInfo): void; - setCompilationSettings(opt: CompilerOptions): void; +declare namespace ts { + /** + * Updates the existing missing file watches with the new set of missing files after new program is created + */ + function updateMissingFilePathsWatch(program: Program, missingFileWatches: Map, createMissingFileWatch: (missingFilePath: Path) => FileWatcher): void; + interface WildcardDirectoryWatcher { + watcher: FileWatcher; + flags: WatchDirectoryFlags; } + /** + * Updates the existing wild card directory watches with the new set of wild card directories from the config file + * after new program is created because the config file was reloaded or program was created first time from the config file + * Note that there is no need to call this function when the program is updated with additional files without reloading config files, + * as wildcard directories wont change unless reloading config file + */ + function updateWatchingWildcardDirectories(existingWatchedForWildcards: Map, wildcardDirectories: Map, watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher): void; } declare namespace ts.server { interface ITypingsInstaller { @@ -7065,40 +7008,6 @@ declare namespace ts.server { onProjectClosed(project: Project): void; } } -declare namespace ts.server { - function shouldEmitFile(scriptInfo: ScriptInfo): boolean; - /** - * An abstract file info that maintains a shape signature. - */ - class BuilderFileInfo { - readonly scriptInfo: ScriptInfo; - readonly project: Project; - private lastCheckedShapeSignature; - constructor(scriptInfo: ScriptInfo, project: Project); - isExternalModuleOrHasOnlyAmbientExternalModules(): boolean; - /** - * For script files that contains only ambient external modules, although they are not actually external module files, - * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, - * there are no point to rebuild all script files if these special files have changed. However, if any statement - * in the file is not ambient external module, we treat it as a regular script file. - */ - private containsOnlyAmbientModules(sourceFile); - private computeHash(text); - private getSourceFile(); - /** - * @return {boolean} indicates if the shape signature has changed since last update. - */ - updateShapeSignature(): boolean; - } - interface Builder { - readonly project: Project; - getFilesAffectedBy(scriptInfo: ScriptInfo): string[]; - onProjectUpdateGraph(): void; - emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean; - clear(): void; - } - function createBuilder(project: Project): Builder; -} declare namespace ts.server { enum ProjectKind { Inferred = 0, @@ -7132,13 +7041,19 @@ declare namespace ts.server { typescript: typeof ts; }): PluginModule; } - abstract class Project { - private readonly projectName; + /** + * The project root can be script info - if root is present, + * or it could be just normalized path if root wasnt present on the host(only for non inferred project) + */ + type ProjectRoot = ScriptInfo | NormalizedPath; + abstract class Project implements LanguageServiceHost, ModuleResolutionHost { + readonly projectName: string; readonly projectKind: ProjectKind; readonly projectService: ProjectService; private documentRegistry; private compilerOptions; compileOnSaveEnabled: boolean; + directoryStructureHost: DirectoryStructureHost; private rootFiles; private rootFilesMap; private program; @@ -7148,8 +7063,9 @@ declare namespace ts.server { private lastCachedUnresolvedImportsList; protected languageService: LanguageService; languageServiceEnabled: boolean; - protected lsHost: LSHost; - builder: Builder; + readonly trace?: (s: string) => void; + readonly realpath?: (path: string) => string; + private builder; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -7175,13 +7091,29 @@ declare namespace ts.server { */ private projectStateVersion; private typingFiles; - protected projectErrors: ReadonlyArray; - typesVersion: number; isNonTsProject(): boolean; isJsOnlyProject(): boolean; getCachedUnresolvedImportsPerFile_TestOnly(): UnresolvedImportsMap; static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {}; - constructor(projectName: string, projectKind: ProjectKind, projectService: ProjectService, documentRegistry: DocumentRegistry, hasExplicitListOfFiles: boolean, languageServiceEnabled: boolean, compilerOptions: CompilerOptions, compileOnSaveEnabled: boolean); + getCompilationSettings(): CompilerOptions; + getNewLine(): string; + getProjectVersion(): string; + getScriptFileNames(): string[]; + private getOrCreateScriptInfoAndAttachToProject(fileName); + getScriptKind(fileName: string): ScriptKind; + getScriptVersion(filename: string): string; + getScriptSnapshot(filename: string): IScriptSnapshot; + getCancellationToken(): HostCancellationToken; + getCurrentDirectory(): string; + getDefaultLibFileName(): string; + useCaseSensitiveFileNames(): boolean; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + readFile(fileName: string): string | undefined; + fileExists(file: string): boolean; + resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModuleFull[]; + resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + directoryExists(path: string): boolean; + getDirectories(path: string): string[]; private setInternalCompilerOptionsForEmittingJsFiles(); /** * Get the errors that dont have any file name associated @@ -7189,8 +7121,12 @@ declare namespace ts.server { getGlobalProjectErrors(): ReadonlyArray; getAllProjectErrors(): ReadonlyArray; getLanguageService(ensureSynchronized?: boolean): LanguageService; + private ensureBuilder(); getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[]; - getProjectVersion(): string; + /** + * Returns true if emit was conducted + */ + emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean; enableLanguageService(): void; disableLanguageService(): void; getProjectName(): string; @@ -7198,24 +7134,22 @@ declare namespace ts.server { abstract getTypeAcquisition(): TypeAcquisition; getExternalFiles(): SortedReadonlyArray; getSourceFile(path: Path): SourceFile; - updateTypes(): void; close(): void; - getCompilerOptions(): CompilerOptions; + isClosed(): boolean; hasRoots(): boolean; getRootFiles(): NormalizedPath[]; - getRootFilesLSHost(): string[]; getRootScriptInfos(): ScriptInfo[]; getScriptInfos(): ScriptInfo[]; - getFileEmitOutput(info: ScriptInfo, emitOnlyDtsFiles: boolean): EmitOutput; + private getFileEmitOutput(sourceFile, emitOnlyDtsFiles, isDetailed); getExcludedFiles(): ReadonlyArray; getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean): NormalizedPath[]; hasConfigFile(configFilePath: NormalizedPath): boolean; - getAllEmittableFiles(): string[]; containsScriptInfo(info: ScriptInfo): boolean; containsFile(filename: NormalizedPath, requireOpen?: boolean): boolean; isRoot(info: ScriptInfo): boolean; addRoot(info: ScriptInfo): void; - removeFile(info: ScriptInfo, detachFromProject?: boolean): void; + addMissingFileRoot(fileName: NormalizedPath): void; + removeFile(info: ScriptInfo, fileExists: boolean, detachFromProject: boolean): void; registerFileUpdate(fileName: string): void; markAsDirty(): void; private extractUnresolvedImportsFromSourceFile(file, result); @@ -7226,14 +7160,14 @@ declare namespace ts.server { updateGraph(): boolean; private setTypings(typings); private updateGraphWorker(); - isWatchedMissingFile(path: Path): boolean; - getScriptInfoLSHost(fileName: string): ScriptInfo; + private detachScriptInfoFromProject(uncheckedFileName); + private addMissingFileWatcher(missingFilePath); + private isWatchedMissingFile(path); getScriptInfoForNormalizedPath(fileName: NormalizedPath): ScriptInfo; getScriptInfo(uncheckedFileName: string): ScriptInfo; - filesToString(): string; + filesToString(writeProjectFileNames: boolean): string; setCompilerOptions(compilerOptions: CompilerOptions): void; reloadScript(filename: NormalizedPath, tempFileName?: NormalizedPath): boolean; - getReferencedFiles(path: Path): Path[]; protected removeRoot(info: ScriptInfo): void; } /** @@ -7246,10 +7180,9 @@ declare namespace ts.server { private _isJsInferredProject; toggleJsInferredProject(isJsInferredProject: boolean): void; setCompilerOptions(options?: CompilerOptions): void; - directoriesWatchedForTsconfig: string[]; - constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, projectRootPath?: string); addRoot(info: ScriptInfo): void; removeRoot(info: ScriptInfo): void; + isProjectWithSingleRoot(): boolean; getProjectRootPath(): string; close(): void; getTypeAcquisition(): TypeAcquisition; @@ -7260,35 +7193,40 @@ declare namespace ts.server { * Otherwise it will create an InferredProject. */ class ConfiguredProject extends Project { - private wildcardDirectories; compileOnSaveEnabled: boolean; private typeAcquisition; - private projectFileWatcher; - private directoryWatcher; private directoriesWatchedForWildcards; - private typeRootsWatchers; readonly canonicalConfigFilePath: NormalizedPath; private plugins; /** Used for configured projects which may have multiple open roots */ openRefCount: number; - constructor(configFileName: NormalizedPath, projectService: ProjectService, documentRegistry: DocumentRegistry, hasExplicitListOfFiles: boolean, compilerOptions: CompilerOptions, wildcardDirectories: Map, languageServiceEnabled: boolean, compileOnSaveEnabled: boolean); - getConfigFilePath(): string; + private projectErrors; + /** + * If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph + * @returns: true if set of files in the project stays the same and false - otherwise. + */ + updateGraph(): boolean; + getConfigFilePath(): NormalizedPath; enablePlugins(): void; private enablePlugin(pluginConfigEntry, searchPaths); private enableProxy(pluginModuleFactory, configEntry); getProjectRootPath(): string; - setProjectErrors(projectErrors: ReadonlyArray): void; + /** + * Get the errors that dont have any file name associated + */ + getGlobalProjectErrors(): ReadonlyArray; + /** + * Get all the project errors + */ + getAllProjectErrors(): ReadonlyArray; + setProjectErrors(projectErrors: Diagnostic[]): void; setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void; getTypeAcquisition(): TypeAcquisition; getExternalFiles(): SortedReadonlyArray; - watchConfigFile(callback: (project: ConfiguredProject) => void): void; - watchTypeRoots(callback: (project: ConfiguredProject, path: string) => void): void; - watchConfigDirectory(callback: (project: ConfiguredProject, path: string) => void): void; - watchWildcards(callback: (project: ConfiguredProject, path: string) => void): void; - stopWatchingDirectory(): void; close(): void; addOpenRef(): void; deleteOpenRef(): number; + hasOpenRef(): boolean; getEffectiveTypeRoots(): string[]; } /** @@ -7301,25 +7239,22 @@ declare namespace ts.server { private readonly projectFilePath; excludedFiles: ReadonlyArray; private typeAcquisition; - constructor(externalProjectName: string, projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, languageServiceEnabled: boolean, compileOnSaveEnabled: boolean, projectFilePath?: string); getExcludedFiles(): ReadonlyArray; getProjectRootPath(): string; getTypeAcquisition(): TypeAcquisition; - setProjectErrors(projectErrors: ReadonlyArray): void; setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void; } } declare namespace ts.server { const maxProgramSizeForNonTsFiles: number; - const ContextEvent = "context"; + const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground"; const ConfigFileDiagEvent = "configFileDiag"; const ProjectLanguageServiceStateEvent = "projectLanguageServiceState"; const ProjectInfoTelemetryEvent = "projectInfo"; - interface ContextEvent { - eventName: typeof ContextEvent; + interface ProjectsUpdatedInBackgroundEvent { + eventName: typeof ProjectsUpdatedInBackgroundEvent; data: { - project: Project; - fileName: NormalizedPath; + openFiles: string[]; }; } interface ConfigFileDiagEvent { @@ -7376,7 +7311,7 @@ declare namespace ts.server { readonly tsx: number; readonly dts: number; } - type ProjectServiceEvent = ContextEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent; + type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent; interface ProjectServiceEventHandler { (event: ProjectServiceEvent): void; } @@ -7444,21 +7379,33 @@ declare namespace ts.server { /** * projects specified by a tsconfig.json file */ - readonly configuredProjects: ConfiguredProject[]; + readonly configuredProjects: Map; /** * list of open files */ readonly openFiles: ScriptInfo[]; private compilerOptionsForInferredProjects; private compilerOptionsForInferredProjectsPerProjectRoot; + /** + * Project size for configured or external projects + */ private readonly projectToSizeMap; - private readonly directoryWatchers; + /** + * This is a map of config file paths existance that doesnt need query to disk + * - The entry can be present because there is inferred project that needs to watch addition of config file to directory + * In this case the exists could be true/false based on config file is present or not + * - Or it is present if we have configured project open with config file at that location + * In this case the exists property is always true + */ + private readonly configFileExistenceInfoCache; private readonly throttledOperations; private readonly hostConfiguration; private safelist; private changedFiles; + private pendingProjectUpdates; + private pendingInferredProjectUpdate; + readonly currentDirectory: string; readonly toCanonicalFileName: (f: string) => string; - lastDeletedFile: ScriptInfo; readonly host: ServerHost; readonly logger: Logger; readonly cancellationToken: HostCancellationToken; @@ -7474,90 +7421,137 @@ declare namespace ts.server { /** Tracks projects that we have already sent telemetry for. */ private readonly seenProjects; constructor(opts: ProjectServiceOptions); - ensureInferredProjectsUpToDate_TestOnly(): void; - getCompilerOptionsForInferredProjects(): CompilerOptions; - onUpdateLanguageServiceStateForProject(project: Project, languageServiceEnabled: boolean): void; + private createWatcherLog(watchType, project); + toPath(fileName: string): Path; private loadTypesMap(); updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void; + private delayInferredProjectsRefresh(); + private delayUpdateProjectGraph(project); + private sendProjectsUpdatedInBackgroundEvent(); + private delayUpdateProjectGraphs(projects); setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void; - stopWatchingDirectory(directory: string): void; - findProject(projectName: string): Project; - getDefaultProjectForFile(fileName: NormalizedPath, refreshInferredProjects: boolean): Project; - private ensureInferredProjectsUpToDate(); + findProject(projectName: string): Project | undefined; + getDefaultProjectForFile(fileName: NormalizedPath, ensureProject: boolean): Project; + getScriptInfoEnsuringProjectsUptoDate(uncheckedFileName: string): ScriptInfo; + /** + * Ensures the project structures are upto date + * This means, + * - if there are changedFiles (the files were updated but their containing project graph was not upto date), + * their project graph is updated + * - If there are pendingProjectUpdates (scheduled to be updated with delay so they can batch update the graph if there are several changes in short time span) + * their project graph is updated + * - If there were project graph updates and/or there was pending inferred project update and/or called forced the inferred project structure refresh + * Inferred projects are created/updated/deleted based on open files states + * @param forceInferredProjectsRefresh when true updates the inferred projects even if there is no pending work to update the files/project structures + */ + private ensureProjectStructuresUptoDate(forceInferredProjectsRefresh?); private findContainingExternalProject(fileName); getFormatCodeOptions(file?: NormalizedPath): FormatCodeSettings; private updateProjectGraphs(projects); - private onSourceFileChanged(fileName); + private onSourceFileChanged(fileName, eventKind); private handleDeletedFile(info); - private onTypeRootFileChanged(project, fileName); + private onConfigChangedForConfiguredProject(project, eventKind); /** - * This is the callback function when a watched directory has added or removed source code files. - * @param project the project that associates with this directory watcher - * @param fileName the absolute file name that changed in watched directory + * This is the callback function for the config file add/remove/change at any location + * that matters to open script info but doesnt have configured project open + * for the config file */ - private onSourceFileInDirectoryChangedForConfiguredProject(project, fileName); - private handleChangeInSourceFileForConfiguredProject(project, triggerFile); - private onConfigChangedForConfiguredProject(project); - /** - * This is the callback function when a watched directory has an added tsconfig file. - */ - private onConfigFileAddedForInferredProject(fileName); - private getCanonicalFileName(fileName); + private onConfigFileChangeForOpenScriptInfo(configFileName, eventKind); private removeProject(project); - private assignScriptInfoToInferredProjectIfNecessary(info, addToListOfOpenFiles, projectRootPath?); + private addToListOfOpenFiles(info); /** * Remove this file from the set of open, non-configured files. * @param info The file that has been closed or newly configured */ private closeOpenFile(info); private deleteOrphanScriptInfoNotInAnyProject(); + private configFileExists(configFileName, canonicalConfigFilePath, info); + private setConfigFileExistenceByNewConfiguredProject(project); /** - * This function tries to search for a tsconfig.json for the given file. If we found it, - * we first detect if there is already a configured project created for it: if so, we re-read - * the tsconfig file content and update the project; otherwise we create a new one. + * Returns true if the configFileExistenceInfo is needed/impacted by open files that are root of inferred project */ - private openOrUpdateConfiguredProjectForFile(fileName, projectRootPath?); - private findConfigFile(searchPath, projectRootPath?); + private configFileExistenceImpactsRootOfInferredProject(configFileExistenceInfo); + private setConfigFileExistenceInfoByClosedConfiguredProject(closedProject); + private logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, status); + /** + * Create the watcher for the configFileExistenceInfo + */ + private createConfigFileWatcherOfConfigFileExistence(configFileName, canonicalConfigFilePath, configFileExistenceInfo); + /** + * Close the config file watcher in the cached ConfigFileExistenceInfo + * if there arent any open files that are root of inferred project + */ + private closeConfigFileWatcherOfConfigFileExistenceInfo(configFileExistenceInfo); + /** + * This is called on file close, so that we stop watching the config file for this script info + */ + private stopWatchingConfigFilesForClosedScriptInfo(info); + /** + * This function tries to search for a tsconfig.json for the given file. + * This is different from the method the compiler uses because + * the compiler can assume it will always start searching in the + * current directory (the directory in which tsc was invoked). + * The server must start searching from the directory containing + * the newly opened file. + */ + private forEachConfigFileLocation(info, action, projectRootPath?); + /** + * This function tries to search for a tsconfig.json for the given file. + * This is different from the method the compiler uses because + * the compiler can assume it will always start searching in the + * current directory (the directory in which tsc was invoked). + * The server must start searching from the directory containing + * the newly opened file. + */ + private getConfigFileNameForFile(info, projectRootPath?); private printProjects(); private findConfiguredProjectByProjectName(configFileName); + private getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath); private findExternalProjectByProjectName(projectFileName); - private convertConfigFileContentToProjectOptions(configFilename); + private convertConfigFileContentToProjectOptions(configFilename, cachedDirectoryStructureHost); private exceededTotalSizeLimitForNonTsFiles(name, options, fileNames, propertyReader); - private createAndAddExternalProject(projectFileName, files, options, typeAcquisition); + private createExternalProject(projectFileName, files, options, typeAcquisition); private sendProjectTelemetry(projectKey, project, projectOptions?); - private reportConfigFileDiagnostics(configFileName, diagnostics, triggerFile); - private createAndAddConfiguredProject(configFileName, projectOptions, configFileErrors, clientFileName?); - private watchConfigDirectoryForProject(project, options); - private addFilesToProjectAndUpdateGraph(project, files, propertyReader, clientFileName, typeAcquisition, configFileErrors); - private openConfigFile(configFileName, clientFileName?); - private updateNonInferredProject(project, newUncheckedFiles, propertyReader, newOptions, newTypeAcquisition, compileOnSave, configFileErrors); - private updateConfiguredProject(project); - private getOrCreateInferredProjectForProjectRootPathIfEnabled(root, projectRootPath); + private addFilesToNonInferredProjectAndUpdateGraph(project, files, propertyReader, typeAcquisition); + private createConfiguredProject(configFileName); + private updateNonInferredProjectFiles(project, files, propertyReader); + private updateNonInferredProject(project, newUncheckedFiles, propertyReader, newOptions, newTypeAcquisition, compileOnSave); + private getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath); private getOrCreateSingleInferredProjectIfEnabled(); - private createInferredProject(isSingleInferredProject?, projectRootPath?); - createInferredProjectWithRootFileIfNecessary(root: ScriptInfo, projectRootPath?: string): InferredProject; - /** - * @param uncheckedFileName is absolute pathname - * @param fileContent is a known version of the file content that is more up to date than the one on disk - */ - getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind): ScriptInfo; + private createInferredProject(rootDirectoryForResolution, isSingleInferredProject?, projectRootPath?); getScriptInfo(uncheckedFileName: string): ScriptInfo; - watchClosedScriptInfo(info: ScriptInfo): void; - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, isDynamic?: boolean): ScriptInfo; + private watchClosedScriptInfo(info); + private stopWatchingScriptInfo(info); + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost): ScriptInfo; getScriptInfoForNormalizedPath(fileName: NormalizedPath): ScriptInfo; getScriptInfoForPath(fileName: Path): ScriptInfo; setHostConfiguration(args: protocol.ConfigureRequestArguments): void; closeLog(): void; /** * This function rebuilds the project for every file opened by the client + * This does not reload contents of open files from disk. But we could do that if needed */ reloadProjects(): void; + private delayReloadConfiguredProjectForFiles(configFileExistenceInfo, ignoreIfNotRootOfInferredProject); /** - * This function is to update the project structure for every projects. + * This function goes through all the openFiles and tries to file the config file for them. + * If the config file is found and it refers to existing project, it reloads it either immediately + * or schedules it for reload depending on delayReload option + * If the there is no existing project it just opens the configured project for the config file + */ + private reloadConfiguredProjectForFiles(openFiles, delayReload); + /** + * Remove the root of inferred project if script info is part of another project + */ + private removeRootOfInferredProjectIfNowPartOfOtherProject(info); + /** + * This function is to update the project structure for every inferred project. * It is called on the premise that all the configured projects are * up to date. + * This will go through open files and assign them to inferred project if open file is not part of any other project + * After that all the inferred project graphs are updated */ - refreshInferredProjects(): void; + private refreshInferredProjects(); /** * Open file whose contents is managed by the client * @param filename is absolute pathname diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ee26706ecea..d41db2eb413 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2405,22 +2405,23 @@ declare namespace ts { Dts = ".d.ts", Js = ".js", Jsx = ".jsx", + Json = ".json", } interface ResolvedModuleWithFailedLookupLocations { - resolvedModule: ResolvedModuleFull | undefined; + readonly resolvedModule: ResolvedModuleFull | undefined; } interface ResolvedTypeReferenceDirective { primary: boolean; - resolvedFileName?: string; + resolvedFileName: string | undefined; packageId?: PackageId; } interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { - resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; - failedLookupLocations: string[]; + readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; + readonly failedLookupLocations: ReadonlyArray; } interface CompilerHost extends ModuleResolutionHost { - getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined; - getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined; + getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; + getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getCancellationToken?(): CancellationToken; getDefaultLibFileName(options: CompilerOptions): string; getDefaultLibLocation?(): string; @@ -2430,7 +2431,7 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ @@ -2683,14 +2684,26 @@ declare namespace ts { callback: FileWatcherCallback; mtime?: Date; } - interface System { - args: string[]; + /** + * Partial interface of the System thats needed to support the caching of directory structure + */ + interface DirectoryStructureHost { newLine: string; useCaseSensitiveFileNames: boolean; write(s: string): void; readFile(path: string, encoding?: string): string | undefined; - getFileSize?(path: string): number; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + exit(exitCode?: number): void; + } + interface System extends DirectoryStructureHost { + args: string[]; + getFileSize?(path: string): number; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching @@ -2698,13 +2711,7 @@ declare namespace ts { watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; getExecutingFilePath(): string; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -2712,7 +2719,6 @@ declare namespace ts { */ createHash?(data: string): string; getMemoryUsage?(): number; - exit(exitCode?: number): void; realpath?(path: string): string; setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout?(timeoutId: any): void; @@ -2720,10 +2726,6 @@ declare namespace ts { interface FileWatcher { close(): void; } - interface DirectoryWatcher extends FileWatcher { - directoryName: string; - referenceCount: number; - } function getNodeMajorVersion(): number; let sys: System; } @@ -3657,6 +3659,23 @@ declare namespace ts { declare namespace ts { function createPrinter(printerOptions?: PrinterOptions, handlers?: PrintHandlers): Printer; } +declare namespace ts { + interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + interface EmitOutputDetailed extends EmitOutput { + diagnostics: Diagnostic[]; + sourceMaps: SourceMapData[]; + emittedSourceFiles: SourceFile[]; + } + interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } + function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed; +} declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; function resolveTripleslashReference(moduleName: string, containingFile: string): string; @@ -3668,6 +3687,7 @@ declare namespace ts { getNewLine(): string; } function formatDiagnostics(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; + function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; /** @@ -3856,7 +3876,7 @@ declare namespace ts { readFile?(path: string, encoding?: string): string | undefined; fileExists?(path: string): boolean; getTypeRootsVersion?(): number; - resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; directoryExists?(directoryName: string): boolean; getDirectories?(directoryName: string): string[]; @@ -3914,6 +3934,7 @@ declare namespace ts { getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed; getProgram(): Program; dispose(): void; } @@ -4270,20 +4291,11 @@ declare namespace ts { */ autoCollapse: boolean; } - interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } enum OutputFileType { JavaScript = 0, SourceMap = 1, Declaration = 2, } - interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } enum EndOfLineState { None = 0, InMultiLineCommentTrivia = 1, diff --git a/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols b/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols index c5363a2ba3f..0537638aa10 100644 --- a/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols +++ b/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols @@ -8,18 +8,28 @@ * @param {(number[])?} c - number[] | null * @param {?...number} d - number[] | null * @param {...?number} e - (number | null)[] - * @param {...number?} f - (number | null)[] + * @param {...number?} f - number[] | null + * @param {...number!?} g - number[] | null + * @param {...number?!} h - number[] | null + * @param {...number[]} i - number[][] + * @param {...number![]?} j - number[][] | null + * @param {...number?[]!} k - (number[] | null)[] */ -function f(x, y, z, a, b, c, d, e, f) { +function f(x, y, z, a, b, c, d, e, f, g, h, i, j, k) { >f : Symbol(f, Decl(prefixPostfix.js, 0, 0)) ->x : Symbol(x, Decl(prefixPostfix.js, 11, 11)) ->y : Symbol(y, Decl(prefixPostfix.js, 11, 13)) ->z : Symbol(z, Decl(prefixPostfix.js, 11, 16)) ->a : Symbol(a, Decl(prefixPostfix.js, 11, 19)) ->b : Symbol(b, Decl(prefixPostfix.js, 11, 22)) ->c : Symbol(c, Decl(prefixPostfix.js, 11, 25)) ->d : Symbol(d, Decl(prefixPostfix.js, 11, 28)) ->e : Symbol(e, Decl(prefixPostfix.js, 11, 31)) ->f : Symbol(f, Decl(prefixPostfix.js, 11, 34)) +>x : Symbol(x, Decl(prefixPostfix.js, 16, 11)) +>y : Symbol(y, Decl(prefixPostfix.js, 16, 13)) +>z : Symbol(z, Decl(prefixPostfix.js, 16, 16)) +>a : Symbol(a, Decl(prefixPostfix.js, 16, 19)) +>b : Symbol(b, Decl(prefixPostfix.js, 16, 22)) +>c : Symbol(c, Decl(prefixPostfix.js, 16, 25)) +>d : Symbol(d, Decl(prefixPostfix.js, 16, 28)) +>e : Symbol(e, Decl(prefixPostfix.js, 16, 31)) +>f : Symbol(f, Decl(prefixPostfix.js, 16, 34)) +>g : Symbol(g, Decl(prefixPostfix.js, 16, 37)) +>h : Symbol(h, Decl(prefixPostfix.js, 16, 40)) +>i : Symbol(i, Decl(prefixPostfix.js, 16, 43)) +>j : Symbol(j, Decl(prefixPostfix.js, 16, 46)) +>k : Symbol(k, Decl(prefixPostfix.js, 16, 49)) } diff --git a/tests/baselines/reference/jsdocPrefixPostfixParsing.types b/tests/baselines/reference/jsdocPrefixPostfixParsing.types index 46b15304bfa..b5ddec68cba 100644 --- a/tests/baselines/reference/jsdocPrefixPostfixParsing.types +++ b/tests/baselines/reference/jsdocPrefixPostfixParsing.types @@ -8,10 +8,15 @@ * @param {(number[])?} c - number[] | null * @param {?...number} d - number[] | null * @param {...?number} e - (number | null)[] - * @param {...number?} f - (number | null)[] + * @param {...number?} f - number[] | null + * @param {...number!?} g - number[] | null + * @param {...number?!} h - number[] | null + * @param {...number[]} i - number[][] + * @param {...number![]?} j - number[][] | null + * @param {...number?[]!} k - (number[] | null)[] */ -function f(x, y, z, a, b, c, d, e, f) { ->f : (x: number[], y: number[], z: number[], a: (number | null)[], b: number[] | null, c: number[] | null, d: number[] | null, ...e: (number | null)[], ...f: (number | null)[]) => void +function f(x, y, z, a, b, c, d, e, f, g, h, i, j, k) { +>f : (x: number[], y: number[], z: number[], a: (number | null)[], b: number[] | null, c: number[] | null, d: number[] | null, ...e: (number | null)[], f: number[] | null, g: number[] | null, h: number[] | null, i: number[][], j: number[][] | null, k: (number[] | null)[]) => void >x : number[] >y : number[] >z : number[] @@ -20,6 +25,11 @@ function f(x, y, z, a, b, c, d, e, f) { >c : number[] | null >d : number[] | null >e : (number | null)[] ->f : (number | null)[] +>f : number[] | null +>g : number[] | null +>h : number[] | null +>i : number[][] +>j : number[][] | null +>k : (number[] | null)[] } diff --git a/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts b/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts index 707ae5a02a2..2db97d83cea 100644 --- a/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts +++ b/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts @@ -15,7 +15,12 @@ * @param {(number[])?} c - number[] | null * @param {?...number} d - number[] | null * @param {...?number} e - (number | null)[] - * @param {...number?} f - (number | null)[] + * @param {...number?} f - number[] | null + * @param {...number!?} g - number[] | null + * @param {...number?!} h - number[] | null + * @param {...number[]} i - number[][] + * @param {...number![]?} j - number[][] | null + * @param {...number?[]!} k - (number[] | null)[] */ -function f(x, y, z, a, b, c, d, e, f) { +function f(x, y, z, a, b, c, d, e, f, g, h, i, j, k) { } diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts index 255976c7767..a259b2dd719 100644 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax27.ts @@ -1,4 +1,4 @@ // @strict: true /// ////type T = [|...number?|]; -verify.rangeAfterCodeFix("(number | null)[]"); +verify.rangeAfterCodeFix("number[] | null", /*includeWhiteSpace*/ false, /*errorCode*/ 8020, 0);