diff --git a/package.json b/package.json index d63d8904c5836..ffdefcd2871ec 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typescript", "author": "Microsoft Corp.", "homepage": "https://www.typescriptlang.org/", - "version": "4.1.3", + "version": "4.1.3-lsif.1", "license": "Apache-2.0", "description": "TypeScript is a language for application scale JavaScript development", "keywords": [ @@ -128,6 +128,5 @@ "@microsoft/typescript-etw": false, "source-map-support": false, "inspector": false - }, - "dependencies": {} + } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ac7a4475fdc4..07c9583e45ecc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -381,6 +381,9 @@ namespace ts { // extra cost of calling `getParseTreeNode` when calling these functions from inside the // checker. const checker: TypeChecker = { + setSymbolChainCache: (cache: SymbolChainCache | undefined): void => { + nodeBuilder.setSymbolChainCache(cache); + }, getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount, @@ -4323,7 +4326,9 @@ namespace ts { } function createNodeBuilder() { + let symbolChainCache: SymbolChainCache | undefined; return { + setSymbolChainCache: (cache: SymbolChainCache | undefined): void => { symbolChainCache = cache }, typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => typeToTypeNodeHelper(type, context)), indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => @@ -4341,7 +4346,7 @@ namespace ts { typeParameterToDeclaration: (parameter: TypeParameter, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => typeParameterToDeclaration(parameter, context)), symbolTableToDeclarationStatements: (symbolTable: SymbolTable, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker, bundled?: boolean) => - withContext(enclosingDeclaration, flags, tracker, context => symbolTableToDeclarationStatements(symbolTable, context, bundled)), + withContext(enclosingDeclaration, flags, tracker, context => symbolTableToDeclarationStatements(symbolTable, context, bundled)) }; function withContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined, tracker: SymbolTracker | undefined, cb: (context: NodeBuilderContext) => T): T | undefined { @@ -4361,6 +4366,7 @@ namespace ts { isSourceOfProjectReferenceRedirect: fileName => host.isSourceOfProjectReferenceRedirect(fileName), fileExists: fileName => host.fileExists(fileName), } : undefined }, + cache: symbolChainCache, encounteredError: false, visitedTypes: undefined, symbolDepth: undefined, @@ -5333,6 +5339,30 @@ namespace ts { /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined { + let key: SymbolChainCacheKey | undefined; + let result: Symbol[] | undefined; + if (context.cache) { + key = { + symbol, + enclosingDeclaration: context.enclosingDeclaration, + flags: context.flags, + meaning: meaning, + yieldModuleSymbol: yieldModuleSymbol, + endOfChain: endOfChain + } + result = context.cache.lookup(key); + if (result) { + return result; + } + } + result = doGetSymbolChain(symbol, meaning, endOfChain); + if (result && key && context.cache) { + context.cache.cache(key, result); + } + return result; + } + + function doGetSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined { let accessibleSymbolChain = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, !!(context.flags & NodeBuilderFlags.UseOnlyExternalAliasing)); let parentSpecifiers: (string | undefined)[]; if (!accessibleSymbolChain || @@ -7446,6 +7476,7 @@ namespace ts { enclosingDeclaration: Node | undefined; flags: NodeBuilderFlags; tracker: SymbolTracker; + cache: SymbolChainCache | undefined; // State encounteredError: boolean; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 72fc2ab72aae7..f956d6c5d0692 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3919,7 +3919,22 @@ namespace ts { readonly redirectTargetsMap: RedirectTargetsMap; } + export interface SymbolChainCacheKey { + symbol: Symbol; + enclosingDeclaration?: Node; + flags: NodeBuilderFlags; + meaning: SymbolFlags; + yieldModuleSymbol?: boolean; + endOfChain: boolean; + } + + export interface SymbolChainCache { + lookup(key: SymbolChainCacheKey): Symbol[] | undefined; + cache(key: SymbolChainCacheKey, value: Symbol[]): void; + } + export interface TypeChecker { + setSymbolChainCache(cache: SymbolChainCache | undefined): void; getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; diff --git a/src/harness/client.ts b/src/harness/client.ts index 230517b599db9..e357c041c2456 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -163,7 +163,11 @@ namespace ts.server { return { line, character: offset }; } - getQuickInfoAtPosition(fileName: string, position: number): QuickInfo { + getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; + getQuickInfoAtPosition(node: ts.Node, sourceFile?: ts.SourceFile): QuickInfo; + getQuickInfoAtPosition(arg0: string | ts.Node, arg1: number | ts.SourceFile | undefined): QuickInfo { + const fileName = typeof arg0 === "string" ? arg0 : arg1 !== undefined ? (arg1 as ts.SourceFile).fileName : arg0.getSourceFile().fileName; + const position = typeof arg0 === "string" ? arg1 as number : arg0.getStart(arg1 as ts.SourceFile); const args = this.createFileLocationRequestArgs(fileName, position); const request = this.processRequest(CommandNames.Quickinfo, args); @@ -574,7 +578,10 @@ namespace ts.server { })); } - getOutliningSpans(file: string): OutliningSpan[] { + getOutliningSpans(file: string): OutliningSpan[]; + getOutliningSpans(sourceFile: ts.SourceFile): OutliningSpan[]; + getOutliningSpans(arg0: string | ts.SourceFile): OutliningSpan[] { + const file = typeof arg0 === "string" ? arg0 : arg0.fileName; const request = this.processRequest(CommandNames.GetOutliningSpans, { file }); const response = this.processResponse(request); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index cc2f285fc5c8f..df1a215d64065 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -480,8 +480,10 @@ namespace Harness.LanguageService { getCompletionEntrySymbol(): ts.Symbol { throw new Error("getCompletionEntrySymbol not implemented across the shim layer."); } - getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo { - return unwrapJSONCallResult(this.shim.getQuickInfoAtPosition(fileName, position)); + getQuickInfoAtPosition(filename: string, position: number): ts.QuickInfo | undefined; + getQuickInfoAtPosition(node: ts.Node, sourceFile: ts.SourceFile | undefined): ts.QuickInfo | undefined; + getQuickInfoAtPosition(arg0: string | ts.Node, arg1: number | ts.SourceFile | undefined): ts.QuickInfo | undefined { + return unwrapJSONCallResult(this.shim.getQuickInfoAtPosition(arg0 as any, arg1 as any)); } getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): ts.TextSpan { return unwrapJSONCallResult(this.shim.getNameOrDottedNameSpan(fileName, startPos, endPos)); @@ -534,8 +536,8 @@ namespace Harness.LanguageService { getNavigationTree(fileName: string): ts.NavigationTree { return unwrapJSONCallResult(this.shim.getNavigationTree(fileName)); } - getOutliningSpans(fileName: string): ts.OutliningSpan[] { - return unwrapJSONCallResult(this.shim.getOutliningSpans(fileName)); + getOutliningSpans(arg0: string | ts.SourceFile): ts.OutliningSpan[] { + return unwrapJSONCallResult(this.shim.getOutliningSpans(arg0 as any)); } getTodoComments(fileName: string, descriptors: ts.TodoCommentDescriptor[]): ts.TodoComment[] { return unwrapJSONCallResult(this.shim.getTodoComments(fileName, JSON.stringify(descriptors))); @@ -855,7 +857,6 @@ namespace Harness.LanguageService { create(info: ts.server.PluginCreateInfo) { const proxy = makeDefaultProxy(info); const langSvc: any = info.languageService; - // eslint-disable-next-line only-arrow-functions proxy.getQuickInfoAtPosition = function () { const parts = langSvc.getQuickInfoAtPosition.apply(langSvc, arguments); if (parts.displayParts.length > 0) { diff --git a/src/services/services.ts b/src/services/services.ts index 89292f09cf5b0..a2988cc08ae48 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1567,11 +1567,15 @@ namespace ts { return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences); } - function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined { - synchronizeHostData(); + function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined; + function getQuickInfoAtPosition(node: ts.Node, sourceFile?: ts.SourceFile): QuickInfo | undefined; + function getQuickInfoAtPosition(arg0: string | ts.Node, arg1: number | ts.SourceFile | undefined): QuickInfo | undefined { + synchronizeHostData(); + + const sourceFile: ts.SourceFile = typeof arg0 === 'string' ? getValidSourceFile(arg0) : (arg1 !== undefined) ? arg1 as ts.SourceFile : arg0.getSourceFile(); + const node: ts.Node = typeof arg0 === 'string' ? getTouchingPropertyName(sourceFile, arg1 as number) : arg0; + const position: number = typeof arg1 === 'number' ? arg1 : node.getStart(sourceFile, false); - const sourceFile = getValidSourceFile(fileName); - const node = getTouchingPropertyName(sourceFile, position); if (node === sourceFile) { // Avoid giving quickInfo for the sourceFile as a whole. return undefined; @@ -1870,11 +1874,13 @@ namespace ts { return ts.getEncodedSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span); } - function getOutliningSpans(fileName: string): OutliningSpan[] { - // doesn't use compiler - no need to synchronize with host - const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return OutliningElementsCollector.collectElements(sourceFile, cancellationToken); - } + function getOutliningSpans(fileName: string): OutliningSpan[]; + function getOutliningSpans(sourceFile: ts.SourceFile): OutliningSpan[]; + function getOutliningSpans(arg0: string | ts.SourceFile): OutliningSpan[] { + // doesn't use compiler - no need to synchronize with host + const sourceFile = typeof arg0 === 'string' ? syntaxTreeCache.getCurrentSourceFile(arg0) : arg0; + return OutliningElementsCollector.collectElements(sourceFile, cancellationToken); + } const braceMatching = new Map(getEntries({ [SyntaxKind.OpenBraceToken]: SyntaxKind.CloseBraceToken, diff --git a/src/services/shims.ts b/src/services/shims.ts index 14f6a61e7478f..eef7207195df6 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -756,12 +756,16 @@ namespace ts { * Computes a string representation of the type at the requested position * in the active file. */ - public getQuickInfoAtPosition(fileName: string, position: number): string { - return this.forwardJSONCall( - `getQuickInfoAtPosition('${fileName}', ${position})`, - () => this.languageService.getQuickInfoAtPosition(fileName, position) - ); - } + public getQuickInfoAtPosition(fileName: string, position: number): string; + public getQuickInfoAtPosition(node: ts.Node, sourceFile: ts.SourceFile | undefined): string; + public getQuickInfoAtPosition(arg0: string | ts.Node, arg1: number | ts.SourceFile | undefined): string { + const fileName = typeof arg0 === "string" ? arg0 : arg1 !== undefined ? (arg1 as ts.SourceFile).fileName : arg0.getSourceFile().fileName; + const position = typeof arg0 === "string" ? arg1 as number : arg0.getStart(arg1 as ts.SourceFile); + return this.forwardJSONCall( + `getQuickInfoAtPosition('${fileName}', ${position})`, + () => this.languageService.getQuickInfoAtPosition(arg0 as any, arg1 as any) + ); + } /// NAMEORDOTTEDNAMESPAN @@ -1017,12 +1021,15 @@ namespace ts { ); } - public getOutliningSpans(fileName: string): string { - return this.forwardJSONCall( - `getOutliningSpans('${fileName}')`, - () => this.languageService.getOutliningSpans(fileName) - ); - } + public getOutliningSpans(fileName: string): string; + public getOutliningSpans(sourceFile: ts.SourceFile): string; + public getOutliningSpans(arg0: string | ts.SourceFile): string { + let fileName = typeof arg0 === "string" ? arg0 : arg0.fileName; + return this.forwardJSONCall( + `getOutliningSpans('${fileName}')`, + () => this.languageService.getOutliningSpans(arg0 as any) + ); + } public getTodoComments(fileName: string, descriptors: string): string { return this.forwardJSONCall( diff --git a/src/services/types.ts b/src/services/types.ts index 7fb1166eb19c8..bb54e018eb8fc 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -446,6 +446,7 @@ namespace ts { * @param position A zero-based index of the character where you want the quick info */ getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined; + getQuickInfoAtPosition(node: ts.Node, sourceFile?: ts.SourceFile): QuickInfo | undefined; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan | undefined; @@ -479,6 +480,7 @@ namespace ts { provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[]; getOutliningSpans(fileName: string): OutliningSpan[]; + getOutliningSpans(sourceFile: ts.SourceFile): OutliningSpan[]; getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[]; getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[]; getIndentationAtPosition(fileName: string, position: number, options: EditorOptions | EditorSettings): number;