mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into literalTypesAlways
This commit is contained in:
@@ -1163,8 +1163,8 @@ namespace ts {
|
||||
currentFlow = finishFlowLabel(postExpressionLabel);
|
||||
}
|
||||
|
||||
function bindInitializedVariableFlow(node: VariableDeclaration | BindingElement) {
|
||||
const name = node.name;
|
||||
function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
|
||||
const name = !isOmittedExpression(node) ? node.name : undefined;
|
||||
if (isBindingPattern(name)) {
|
||||
for (const child of name.elements) {
|
||||
bindInitializedVariableFlow(child);
|
||||
|
||||
+23
-16
@@ -1043,7 +1043,7 @@ namespace ts {
|
||||
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
|
||||
|
||||
if (moduleSymbol) {
|
||||
const exportDefaultSymbol = isShorthandAmbientModule(moduleSymbol.valueDeclaration) ?
|
||||
const exportDefaultSymbol = isShorthandAmbientModuleSymbol(moduleSymbol) ?
|
||||
moduleSymbol :
|
||||
moduleSymbol.exports["export="] ?
|
||||
getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") :
|
||||
@@ -1119,7 +1119,7 @@ namespace ts {
|
||||
if (targetSymbol) {
|
||||
const name = specifier.propertyName || specifier.name;
|
||||
if (name.text) {
|
||||
if (isShorthandAmbientModule(moduleSymbol.valueDeclaration)) {
|
||||
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
|
||||
return moduleSymbol;
|
||||
}
|
||||
|
||||
@@ -2505,8 +2505,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function buildBindingElementDisplay(bindingElement: BindingElement, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
|
||||
if (bindingElement.kind === SyntaxKind.OmittedExpression) {
|
||||
function buildBindingElementDisplay(bindingElement: ArrayBindingElement, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
|
||||
if (isOmittedExpression(bindingElement)) {
|
||||
return;
|
||||
}
|
||||
Debug.assert(bindingElement.kind === SyntaxKind.BindingElement);
|
||||
@@ -3129,7 +3129,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Return the type implied by an object binding pattern
|
||||
function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
|
||||
function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
|
||||
const members = createMap<Symbol>();
|
||||
let hasComputedProperties = false;
|
||||
forEach(pattern.elements, e => {
|
||||
@@ -3160,11 +3160,12 @@ namespace ts {
|
||||
// Return the type implied by an array binding pattern
|
||||
function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
|
||||
const elements = pattern.elements;
|
||||
if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) {
|
||||
const lastElement = lastOrUndefined(elements);
|
||||
if (elements.length === 0 || (!isOmittedExpression(lastElement) && lastElement.dotDotDotToken)) {
|
||||
return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType;
|
||||
}
|
||||
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
|
||||
const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
|
||||
const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
|
||||
let result = createTupleType(elementTypes);
|
||||
if (includePatternInType) {
|
||||
result = cloneTypeReference(result);
|
||||
@@ -3182,8 +3183,8 @@ namespace ts {
|
||||
// the parameter.
|
||||
function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type {
|
||||
return pattern.kind === SyntaxKind.ObjectBindingPattern
|
||||
? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors)
|
||||
: getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors);
|
||||
? getTypeFromObjectBindingPattern(<ObjectBindingPattern>pattern, includePatternInType, reportErrors)
|
||||
: getTypeFromArrayBindingPattern(<ArrayBindingPattern>pattern, includePatternInType, reportErrors);
|
||||
}
|
||||
|
||||
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
|
||||
@@ -3385,7 +3386,7 @@ namespace ts {
|
||||
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModule(<ModuleDeclaration>symbol.valueDeclaration)) {
|
||||
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModuleSymbol(symbol)) {
|
||||
links.type = anyType;
|
||||
}
|
||||
else {
|
||||
@@ -8970,6 +8971,7 @@ namespace ts {
|
||||
const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
|
||||
const declarationContainer = getControlFlowContainer(declaration);
|
||||
let flowContainer = getControlFlowContainer(node);
|
||||
const isOuterVariable = flowContainer !== declarationContainer;
|
||||
// When the control flow originates in a function expression or arrow function and we are referencing
|
||||
// a const variable or parameter from an outer function, we extend the origin of the control flow
|
||||
// analysis to include the immediately enclosing function.
|
||||
@@ -8982,7 +8984,7 @@ namespace ts {
|
||||
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
|
||||
// declaration container are the same).
|
||||
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter ||
|
||||
flowContainer !== declarationContainer || isInAmbientContext(declaration);
|
||||
isOuterVariable || isInAmbientContext(declaration);
|
||||
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
|
||||
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
|
||||
// from declaration to use, and when the variable's declared type doesn't include undefined but the
|
||||
@@ -12437,7 +12439,7 @@ namespace ts {
|
||||
function assignBindingElementTypes(node: VariableLikeDeclaration) {
|
||||
if (isBindingPattern(node.name)) {
|
||||
for (const element of (<BindingPattern>node.name).elements) {
|
||||
if (element.kind !== SyntaxKind.OmittedExpression) {
|
||||
if (!isOmittedExpression(element)) {
|
||||
if (element.name.kind === SyntaxKind.Identifier) {
|
||||
getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element);
|
||||
}
|
||||
@@ -13880,7 +13882,12 @@ namespace ts {
|
||||
pattern: BindingPattern,
|
||||
predicateVariableNode: Node,
|
||||
predicateVariableName: string) {
|
||||
for (const { name } of pattern.elements) {
|
||||
for (const element of pattern.elements) {
|
||||
if (isOmittedExpression(element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = element.name;
|
||||
if (name.kind === SyntaxKind.Identifier &&
|
||||
(<Identifier>name).text === predicateVariableName) {
|
||||
error(predicateVariableNode,
|
||||
@@ -18356,8 +18363,8 @@ namespace ts {
|
||||
|
||||
function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean {
|
||||
let moduleSymbol = resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression);
|
||||
if (!moduleSymbol) {
|
||||
// module not found - be conservative
|
||||
if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) {
|
||||
// If the module is not found or is shorthand, assume that it may export a value.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19990,7 +19997,7 @@ namespace ts {
|
||||
else {
|
||||
const elements = (<BindingPattern>name).elements;
|
||||
for (const element of elements) {
|
||||
if (element.kind !== SyntaxKind.OmittedExpression) {
|
||||
if (!isOmittedExpression(element)) {
|
||||
checkGrammarNameInLetOrConstDeclarations(element.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,9 +554,9 @@ const _super = (function (geti, seti) {
|
||||
|
||||
// Binding patterns
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
return emitObjectBindingPattern(<BindingPattern>node);
|
||||
return emitObjectBindingPattern(<ObjectBindingPattern>node);
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return emitArrayBindingPattern(<BindingPattern>node);
|
||||
return emitArrayBindingPattern(<ArrayBindingPattern>node);
|
||||
case SyntaxKind.BindingElement:
|
||||
return emitBindingElement(<BindingElement>node);
|
||||
|
||||
|
||||
@@ -368,13 +368,13 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createArrayBindingPattern(elements: BindingElement[], location?: TextRange) {
|
||||
export function createArrayBindingPattern(elements: ArrayBindingElement[], location?: TextRange) {
|
||||
const node = <ArrayBindingPattern>createNode(SyntaxKind.ArrayBindingPattern, location);
|
||||
node.elements = createNodeArray(elements);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: BindingElement[]) {
|
||||
export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: ArrayBindingElement[]) {
|
||||
if (node.elements !== elements) {
|
||||
return updateNode(createArrayBindingPattern(elements, node), node);
|
||||
}
|
||||
|
||||
@@ -4815,9 +4815,9 @@ namespace ts {
|
||||
|
||||
// DECLARATIONS
|
||||
|
||||
function parseArrayBindingElement(): BindingElement {
|
||||
function parseArrayBindingElement(): ArrayBindingElement {
|
||||
if (token() === SyntaxKind.CommaToken) {
|
||||
return <BindingElement>createNode(SyntaxKind.OmittedExpression);
|
||||
return <OmittedExpression>createNode(SyntaxKind.OmittedExpression);
|
||||
}
|
||||
const node = <BindingElement>createNode(SyntaxKind.BindingElement);
|
||||
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
|
||||
@@ -4842,16 +4842,16 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseObjectBindingPattern(): BindingPattern {
|
||||
const node = <BindingPattern>createNode(SyntaxKind.ObjectBindingPattern);
|
||||
function parseObjectBindingPattern(): ObjectBindingPattern {
|
||||
const node = <ObjectBindingPattern>createNode(SyntaxKind.ObjectBindingPattern);
|
||||
parseExpected(SyntaxKind.OpenBraceToken);
|
||||
node.elements = parseDelimitedList(ParsingContext.ObjectBindingElements, parseObjectBindingElement);
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseArrayBindingPattern(): BindingPattern {
|
||||
const node = <BindingPattern>createNode(SyntaxKind.ArrayBindingPattern);
|
||||
function parseArrayBindingPattern(): ArrayBindingPattern {
|
||||
const node = <ArrayBindingPattern>createNode(SyntaxKind.ArrayBindingPattern);
|
||||
parseExpected(SyntaxKind.OpenBracketToken);
|
||||
node.elements = parseDelimitedList(ParsingContext.ArrayBindingElements, parseArrayBindingElement);
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
|
||||
@@ -326,12 +326,15 @@ namespace ts {
|
||||
}
|
||||
for (let i = 0; i < numElements; i++) {
|
||||
const element = elements[i];
|
||||
if (name.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
if (isOmittedExpression(element)) {
|
||||
continue;
|
||||
}
|
||||
else if (name.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
// Rewrite element to a declaration with an initializer that fetches property
|
||||
const propName = element.propertyName || <Identifier>element.name;
|
||||
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
|
||||
}
|
||||
else if (element.kind !== SyntaxKind.OmittedExpression) {
|
||||
else {
|
||||
if (!element.dotDotDotToken) {
|
||||
// Rewrite element to a declaration that accesses array element at index i
|
||||
emitBindingElement(element, createElementAccess(value, i));
|
||||
|
||||
@@ -1945,7 +1945,9 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
for (const element of (<BindingPattern>node).elements) {
|
||||
visit(element.name);
|
||||
if (!isOmittedExpression(element)) {
|
||||
visit(element.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2289,7 +2291,9 @@ namespace ts {
|
||||
const name = decl.name;
|
||||
if (isBindingPattern(name)) {
|
||||
for (const element of name.elements) {
|
||||
processLoopVariableDeclaration(element, loopParameters, loopOutParameters);
|
||||
if (!isOmittedExpression(element)) {
|
||||
processLoopVariableDeclaration(element, loopParameters, loopOutParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -660,7 +660,9 @@ namespace ts {
|
||||
function addExportMemberAssignmentsForBindingName(resultStatements: Statement[], name: BindingName): void {
|
||||
if (isBindingPattern(name)) {
|
||||
for (const element of name.elements) {
|
||||
addExportMemberAssignmentsForBindingName(resultStatements, element.name);
|
||||
if (!isOmittedExpression(element)) {
|
||||
addExportMemberAssignmentsForBindingName(resultStatements, element.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1368,7 +1368,11 @@ namespace ts {
|
||||
exportedFunctionDeclarations.push(createDeclarationExport(node));
|
||||
}
|
||||
|
||||
function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean): void {
|
||||
function hoistBindingElement(node: VariableDeclaration | ArrayBindingElement, isExported: boolean): void {
|
||||
if (isOmittedExpression(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = node.name;
|
||||
if (isIdentifier(name)) {
|
||||
hoistVariableDeclaration(getSynthesizedClone(name));
|
||||
@@ -1381,11 +1385,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function hoistExportedBindingElement(node: VariableDeclaration | BindingElement) {
|
||||
function hoistExportedBindingElement(node: VariableDeclaration | ArrayBindingElement) {
|
||||
hoistBindingElement(node, /*isExported*/ true);
|
||||
}
|
||||
|
||||
function hoistNonExportedBindingElement(node: VariableDeclaration | BindingElement) {
|
||||
function hoistNonExportedBindingElement(node: VariableDeclaration | ArrayBindingElement) {
|
||||
hoistBindingElement(node, /*isExported*/ false);
|
||||
}
|
||||
|
||||
|
||||
@@ -2187,7 +2187,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The function expression node.
|
||||
*/
|
||||
function visitFunctionExpression(node: FunctionExpression) {
|
||||
function visitFunctionExpression(node: FunctionExpression): Expression {
|
||||
if (nodeIsMissing(node.body)) {
|
||||
return createOmittedExpression();
|
||||
}
|
||||
|
||||
+12
-4
@@ -688,14 +688,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface BindingPattern extends Node {
|
||||
elements: NodeArray<BindingElement>;
|
||||
elements: NodeArray<BindingElement | ArrayBindingElement>;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.ObjectBindingPattern)
|
||||
export interface ObjectBindingPattern extends BindingPattern { }
|
||||
export interface ObjectBindingPattern extends BindingPattern {
|
||||
elements: NodeArray<BindingElement>;
|
||||
}
|
||||
|
||||
export type ArrayBindingElement = BindingElement | OmittedExpression;
|
||||
|
||||
// @kind(SyntaxKind.ArrayBindingPattern)
|
||||
export interface ArrayBindingPattern extends BindingPattern { }
|
||||
export interface ArrayBindingPattern extends BindingPattern {
|
||||
elements: NodeArray<ArrayBindingElement>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Several node kinds share function-like features such as a signature,
|
||||
@@ -868,7 +874,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.OmittedExpression)
|
||||
export interface OmittedExpression extends Expression { }
|
||||
export interface OmittedExpression extends Expression {
|
||||
_omittedExpressionBrand: any;
|
||||
}
|
||||
|
||||
// Represents an expression that is elided as part of a transformation to emit comments on a
|
||||
// not-emitted node. The 'expression' property of a NotEmittedExpression should be emitted.
|
||||
|
||||
@@ -414,7 +414,11 @@ namespace ts {
|
||||
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
|
||||
}
|
||||
|
||||
export function isShorthandAmbientModule(node: Node): boolean {
|
||||
export function isShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean {
|
||||
return isShorthandAmbientModule(moduleSymbol.valueDeclaration);
|
||||
}
|
||||
|
||||
function isShorthandAmbientModule(node: Node): boolean {
|
||||
// The only kind of module that can be missing a body is a shorthand ambient module.
|
||||
return node.kind === SyntaxKind.ModuleDeclaration && (!(<ModuleDeclaration>node).body);
|
||||
}
|
||||
@@ -3701,6 +3705,12 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.BindingElement;
|
||||
}
|
||||
|
||||
export function isArrayBindingElement(node: Node): node is ArrayBindingElement {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.BindingElement
|
||||
|| kind === SyntaxKind.OmittedExpression;
|
||||
}
|
||||
|
||||
// Expression
|
||||
|
||||
export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression {
|
||||
@@ -3817,6 +3827,10 @@ namespace ts {
|
||||
|| isPartiallyEmittedExpression(node);
|
||||
}
|
||||
|
||||
export function isOmittedExpression(node: Node): node is OmittedExpression {
|
||||
return node.kind === SyntaxKind.OmittedExpression;
|
||||
}
|
||||
|
||||
// Misc
|
||||
|
||||
export function isTemplateSpan(node: Node): node is TemplateSpan {
|
||||
|
||||
@@ -757,7 +757,7 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return updateArrayBindingPattern(<ArrayBindingPattern>node,
|
||||
visitNodes((<ArrayBindingPattern>node).elements, visitor, isBindingElement));
|
||||
visitNodes((<ArrayBindingPattern>node).elements, visitor, isArrayBindingElement));
|
||||
|
||||
case SyntaxKind.BindingElement:
|
||||
return updateBindingElement(<BindingElement>node,
|
||||
|
||||
@@ -954,26 +954,20 @@ namespace FourSlash {
|
||||
assert.equal(actual, expected);
|
||||
}
|
||||
|
||||
public verifyQuickInfoString(negative: boolean, expectedText?: string, expectedDocumentation?: string) {
|
||||
public verifyQuickInfoString(negative: boolean, expectedText: string, expectedDocumentation?: string) {
|
||||
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
const actualQuickInfoText = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.displayParts) : "";
|
||||
const actualQuickInfoDocumentation = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.documentation) : "";
|
||||
|
||||
if (negative) {
|
||||
if (expectedText !== undefined) {
|
||||
assert.notEqual(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
}
|
||||
// TODO: should be '==='?
|
||||
if (expectedDocumentation != undefined) {
|
||||
assert.notEqual(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
if (expectedDocumentation !== undefined) {
|
||||
assert.notEqual(actualQuickInfoDocumentation, expectedDocumentation, this.messageAtLastKnownMarker("quick info doc comment"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (expectedText !== undefined) {
|
||||
assert.equal(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
}
|
||||
// TODO: should be '==='?
|
||||
if (expectedDocumentation != undefined) {
|
||||
assert.equal(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
if (expectedDocumentation !== undefined) {
|
||||
assert.equal(actualQuickInfoDocumentation, expectedDocumentation, this.assertionMessageAtLastKnownMarker("quick info doc"));
|
||||
}
|
||||
}
|
||||
@@ -2969,7 +2963,7 @@ namespace FourSlashInterface {
|
||||
this.state.verifyErrorExistsAfterMarker(markerName, !this.negative, /*after*/ false);
|
||||
}
|
||||
|
||||
public quickInfoIs(expectedText?: string, expectedDocumentation?: string) {
|
||||
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
|
||||
this.state.verifyQuickInfoString(this.negative, expectedText, expectedDocumentation);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,8 +232,8 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
getHost() { return this.host; }
|
||||
getLanguageService(): ts.LanguageService { return ts.createLanguageService(this.host); }
|
||||
getClassifier(): ts.Classifier { return ts.Classifier.createClassifier(); }
|
||||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { return ts.PreProcess.preProcessFile(fileContents, /* readImportFiles */ true, ts.hasJavaScriptFileExtension(fileName)); }
|
||||
getClassifier(): ts.Classifier { return ts.createClassifier(); }
|
||||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { return ts.preProcessFile(fileContents, /* readImportFiles */ true, ts.hasJavaScriptFileExtension(fileName)); }
|
||||
}
|
||||
|
||||
/// Shim adapter
|
||||
@@ -258,7 +258,7 @@ namespace Harness.LanguageService {
|
||||
};
|
||||
this.getModuleResolutionsForFile = (fileName) => {
|
||||
const scriptInfo = this.getScriptInfo(fileName);
|
||||
const preprocessInfo = ts.PreProcess.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
|
||||
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
|
||||
const imports = ts.createMap<string>();
|
||||
for (const module of preprocessInfo.importedFiles) {
|
||||
const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost);
|
||||
@@ -271,7 +271,7 @@ namespace Harness.LanguageService {
|
||||
this.getTypeReferenceDirectiveResolutionsForFile = (fileName) => {
|
||||
const scriptInfo = this.getScriptInfo(fileName);
|
||||
if (scriptInfo) {
|
||||
const preprocessInfo = ts.PreProcess.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
|
||||
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
|
||||
const resolutions = ts.createMap<ts.ResolvedTypeReferenceDirective>();
|
||||
const settings = this.nativeHost.getCompilationSettings();
|
||||
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
|
||||
@@ -408,6 +408,9 @@ namespace Harness.LanguageService {
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails {
|
||||
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName));
|
||||
}
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): 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));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
describe("PreProcessFile:", function () {
|
||||
function test(sourceText: string, readImportFile: boolean, detectJavaScriptImports: boolean, expectedPreProcess: ts.PreProcessedFileInfo): void {
|
||||
const resultPreProcess = ts.PreProcess.preProcessFile(sourceText, readImportFile, detectJavaScriptImports);
|
||||
const resultPreProcess = ts.preProcessFile(sourceText, readImportFile, detectJavaScriptImports);
|
||||
|
||||
assert.equal(resultPreProcess.isLibFile, expectedPreProcess.isLibFile, "Pre-processed file has different value for isLibFile. Expected: " + expectedPreProcess.isLibFile + ". Actual: " + resultPreProcess.isLibFile);
|
||||
|
||||
|
||||
@@ -246,6 +246,10 @@ namespace ts.server {
|
||||
return response.body[0];
|
||||
}
|
||||
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getNavigateToItems(searchValue: string): NavigateToItem[] {
|
||||
const args: protocol.NavtoRequestArgs = {
|
||||
searchValue,
|
||||
|
||||
@@ -1566,7 +1566,7 @@ namespace ts.server {
|
||||
this.setCompilerOptions(defaultOpts);
|
||||
}
|
||||
this.languageService = ts.createLanguageService(this.host, this.documentRegistry);
|
||||
this.classifier = ts.Classifier.createClassifier();
|
||||
this.classifier = ts.createClassifier();
|
||||
}
|
||||
|
||||
setCompilerOptions(opt: ts.CompilerOptions) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* @internal */
|
||||
namespace ts.Classifier {
|
||||
namespace ts {
|
||||
/// Classifier
|
||||
export function createClassifier(): Classifier {
|
||||
const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false);
|
||||
@@ -462,6 +461,7 @@ namespace ts.Classifier {
|
||||
};
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map<string>, span: TextSpan): ClassifiedSpan[] {
|
||||
return convertClassifications(getEncodedSemanticClassifications(typeChecker, cancellationToken, sourceFile, classifiableNames, span));
|
||||
}
|
||||
@@ -486,6 +486,7 @@ namespace ts.Classifier {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getEncodedSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map<string>, span: TextSpan): Classifications {
|
||||
const result: number[] = [];
|
||||
processNode(sourceFile);
|
||||
@@ -614,10 +615,12 @@ namespace ts.Classifier {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getSyntacticClassifications(cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] {
|
||||
return convertClassifications(getEncodedSyntacticClassifications(cancellationToken, sourceFile, span));
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getEncodedSyntacticClassifications(cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications {
|
||||
const spanStart = span.start;
|
||||
const spanLength = span.length;
|
||||
|
||||
@@ -736,6 +736,22 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol {
|
||||
// Compute all the completion symbols again.
|
||||
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
|
||||
if (completionData) {
|
||||
const { symbols, location } = completionData;
|
||||
|
||||
// Find the symbol with the matching entry name.
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
return forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number) {
|
||||
const isJavaScriptFile = isSourceFileJavaScript(sourceFile);
|
||||
|
||||
@@ -1192,7 +1208,7 @@ namespace ts.Completions {
|
||||
}
|
||||
if (canGetType) {
|
||||
typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
|
||||
existingMembers = (<BindingPattern>objectLikeContainer).elements;
|
||||
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
namespace ts {
|
||||
/**
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
|
||||
* of files in the context.
|
||||
* SourceFile objects account for most of the memory usage by the language service. Sharing
|
||||
* the same DocumentRegistry instance between different instances of LanguageService allow
|
||||
* for more efficient memory utilization since all projects will share at least the library
|
||||
* file (lib.d.ts).
|
||||
*
|
||||
* A more advanced use of the document registry is to serialize sourceFile objects to disk
|
||||
* and re-hydrate them when needed.
|
||||
*
|
||||
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
|
||||
* to all subsequent createLanguageService calls.
|
||||
*/
|
||||
export interface DocumentRegistry {
|
||||
/**
|
||||
* Request a stored SourceFile with a given fileName and compilationSettings.
|
||||
* The first call to acquire will call createLanguageServiceSourceFile to generate
|
||||
* the SourceFile if was not found in the registry.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @parm scriptSnapshot Text of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
* @parm version Current version of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
*/
|
||||
acquireDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
acquireDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
/**
|
||||
* Request an updated version of an already existing SourceFile with a given fileName
|
||||
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
|
||||
* to get an updated SourceFile.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @param scriptSnapshot Text of the file.
|
||||
* @param version Current version of the file.
|
||||
*/
|
||||
updateDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
updateDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
|
||||
/**
|
||||
* Informs the DocumentRegistry that a file is not needed any longer.
|
||||
*
|
||||
* Note: It is not allowed to call release on a SourceFile that was not acquired from
|
||||
* this registry originally.
|
||||
*
|
||||
* @param fileName The name of the file to be released
|
||||
* @param compilationSettings The compilation settings used to acquire the file
|
||||
*/
|
||||
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
|
||||
|
||||
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
|
||||
|
||||
reportStats(): string;
|
||||
}
|
||||
|
||||
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
|
||||
|
||||
interface DocumentRegistryEntry {
|
||||
sourceFile: SourceFile;
|
||||
|
||||
// The number of language services that this source file is referenced in. When no more
|
||||
// language services are referencing the file, then the file can be removed from the
|
||||
// registry.
|
||||
languageServiceRefCount: number;
|
||||
owners: string[];
|
||||
}
|
||||
|
||||
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
|
||||
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
|
||||
// for those settings.
|
||||
const buckets = createMap<FileMap<DocumentRegistryEntry>>();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
|
||||
|
||||
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
|
||||
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
let bucket = buckets[key];
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
|
||||
function reportStats() {
|
||||
const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => {
|
||||
const entries = buckets[name];
|
||||
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
|
||||
entries.forEachValue((key, entry) => {
|
||||
sourceFiles.push({
|
||||
name: key,
|
||||
refCount: entry.languageServiceRefCount,
|
||||
references: entry.owners.slice(0)
|
||||
});
|
||||
});
|
||||
sourceFiles.sort((x, y) => y.refCount - x.refCount);
|
||||
return {
|
||||
bucket: name,
|
||||
sourceFiles
|
||||
};
|
||||
});
|
||||
return JSON.stringify(bucketInfoArray, undefined, 2);
|
||||
}
|
||||
|
||||
function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
|
||||
}
|
||||
|
||||
function acquireOrUpdateDocument(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
acquiring: boolean,
|
||||
scriptKind?: ScriptKind): SourceFile {
|
||||
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
|
||||
let entry = bucket.get(path);
|
||||
if (!entry) {
|
||||
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
|
||||
|
||||
// Have never seen this file with these settings. Create a new source file for it.
|
||||
const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind);
|
||||
|
||||
entry = {
|
||||
sourceFile: sourceFile,
|
||||
languageServiceRefCount: 0,
|
||||
owners: []
|
||||
};
|
||||
bucket.set(path, entry);
|
||||
}
|
||||
else {
|
||||
// We have an entry for this file. However, it may be for a different version of
|
||||
// the script snapshot. If so, update it appropriately. Otherwise, we can just
|
||||
// return it as is.
|
||||
if (entry.sourceFile.version !== version) {
|
||||
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version,
|
||||
scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot));
|
||||
}
|
||||
}
|
||||
|
||||
// If we're acquiring, then this is the first time this LS is asking for this document.
|
||||
// Increase our ref count so we know there's another LS using the document. If we're
|
||||
// not acquiring, then that means the LS is 'updating' the file instead, and that means
|
||||
// it has already acquired the document previously. As such, we do not need to increase
|
||||
// the ref count.
|
||||
if (acquiring) {
|
||||
entry.languageServiceRefCount++;
|
||||
}
|
||||
|
||||
return entry.sourceFile;
|
||||
}
|
||||
|
||||
function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return releaseDocumentWithKey(path, key);
|
||||
}
|
||||
|
||||
function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void {
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false);
|
||||
Debug.assert(bucket !== undefined);
|
||||
|
||||
const entry = bucket.get(path);
|
||||
entry.languageServiceRefCount--;
|
||||
|
||||
Debug.assert(entry.languageServiceRefCount >= 0);
|
||||
if (entry.languageServiceRefCount === 0) {
|
||||
bucket.remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
acquireDocument,
|
||||
acquireDocumentWithKey,
|
||||
updateDocument,
|
||||
updateDocumentWithKey,
|
||||
releaseDocument,
|
||||
releaseDocumentWithKey,
|
||||
reportStats,
|
||||
getKeyForCompilationSettings
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
/* @internal */
|
||||
namespace ts.PreProcess {
|
||||
namespace ts {
|
||||
export function preProcessFile(sourceText: string, readImportFiles = true, detectJavaScriptImports = false): PreProcessedFileInfo {
|
||||
const referencedFiles: FileReference[] = [];
|
||||
const typeReferenceDirectives: FileReference[] = [];
|
||||
|
||||
+11
-150
@@ -7,6 +7,7 @@
|
||||
/// <reference path='classifier.ts' />
|
||||
/// <reference path='completions.ts' />
|
||||
/// <reference path='documentHighlights.ts' />
|
||||
/// <reference path='documentRegistry.ts' />
|
||||
/// <reference path='findAllReferences.ts' />
|
||||
/// <reference path='goToDefinition.ts' />
|
||||
/// <reference path='jsDoc.ts' />
|
||||
@@ -668,16 +669,6 @@ namespace ts {
|
||||
scriptKind: ScriptKind;
|
||||
}
|
||||
|
||||
interface DocumentRegistryEntry {
|
||||
sourceFile: SourceFile;
|
||||
|
||||
// The number of language services that this source file is referenced in. When no more
|
||||
// language services are referencing the file, then the file can be removed from the
|
||||
// registry.
|
||||
languageServiceRefCount: number;
|
||||
owners: string[];
|
||||
}
|
||||
|
||||
export interface DisplayPartsSymbolWriter extends SymbolWriter {
|
||||
displayParts(): SymbolDisplayPart[];
|
||||
}
|
||||
@@ -899,142 +890,6 @@ namespace ts {
|
||||
return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, sourceFile.scriptKind);
|
||||
}
|
||||
|
||||
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
|
||||
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
|
||||
// for those settings.
|
||||
const buckets = createMap<FileMap<DocumentRegistryEntry>>();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
|
||||
|
||||
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
|
||||
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
let bucket = buckets[key];
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
|
||||
function reportStats() {
|
||||
const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => {
|
||||
const entries = buckets[name];
|
||||
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
|
||||
entries.forEachValue((key, entry) => {
|
||||
sourceFiles.push({
|
||||
name: key,
|
||||
refCount: entry.languageServiceRefCount,
|
||||
references: entry.owners.slice(0)
|
||||
});
|
||||
});
|
||||
sourceFiles.sort((x, y) => y.refCount - x.refCount);
|
||||
return {
|
||||
bucket: name,
|
||||
sourceFiles
|
||||
};
|
||||
});
|
||||
return JSON.stringify(bucketInfoArray, undefined, 2);
|
||||
}
|
||||
|
||||
function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
|
||||
}
|
||||
|
||||
function acquireOrUpdateDocument(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
acquiring: boolean,
|
||||
scriptKind?: ScriptKind): SourceFile {
|
||||
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
|
||||
let entry = bucket.get(path);
|
||||
if (!entry) {
|
||||
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
|
||||
|
||||
// Have never seen this file with these settings. Create a new source file for it.
|
||||
const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind);
|
||||
|
||||
entry = {
|
||||
sourceFile: sourceFile,
|
||||
languageServiceRefCount: 0,
|
||||
owners: []
|
||||
};
|
||||
bucket.set(path, entry);
|
||||
}
|
||||
else {
|
||||
// We have an entry for this file. However, it may be for a different version of
|
||||
// the script snapshot. If so, update it appropriately. Otherwise, we can just
|
||||
// return it as is.
|
||||
if (entry.sourceFile.version !== version) {
|
||||
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version,
|
||||
scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot));
|
||||
}
|
||||
}
|
||||
|
||||
// If we're acquiring, then this is the first time this LS is asking for this document.
|
||||
// Increase our ref count so we know there's another LS using the document. If we're
|
||||
// not acquiring, then that means the LS is 'updating' the file instead, and that means
|
||||
// it has already acquired the document previously. As such, we do not need to increase
|
||||
// the ref count.
|
||||
if (acquiring) {
|
||||
entry.languageServiceRefCount++;
|
||||
}
|
||||
|
||||
return entry.sourceFile;
|
||||
}
|
||||
|
||||
function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return releaseDocumentWithKey(path, key);
|
||||
}
|
||||
|
||||
function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void {
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false);
|
||||
Debug.assert(bucket !== undefined);
|
||||
|
||||
const entry = bucket.get(path);
|
||||
entry.languageServiceRefCount--;
|
||||
|
||||
Debug.assert(entry.languageServiceRefCount >= 0);
|
||||
if (entry.languageServiceRefCount === 0) {
|
||||
bucket.remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
acquireDocument,
|
||||
acquireDocumentWithKey,
|
||||
updateDocument,
|
||||
updateDocumentWithKey,
|
||||
releaseDocument,
|
||||
releaseDocumentWithKey,
|
||||
reportStats,
|
||||
getKeyForCompilationSettings
|
||||
};
|
||||
}
|
||||
|
||||
class CancellationTokenObject implements CancellationToken {
|
||||
constructor(private cancellationToken: HostCancellationToken) {
|
||||
}
|
||||
@@ -1351,6 +1206,11 @@ namespace ts {
|
||||
return Completions.getCompletionEntryDetails(program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, entryName);
|
||||
}
|
||||
|
||||
function getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol {
|
||||
synchronizeHostData();
|
||||
return Completions.getCompletionEntrySymbol(program.getTypeChecker(), log, program.getCompilerOptions(), getValidSourceFile(fileName), position, entryName);
|
||||
}
|
||||
|
||||
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
|
||||
synchronizeHostData();
|
||||
|
||||
@@ -1601,22 +1461,22 @@ namespace ts {
|
||||
|
||||
function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
|
||||
synchronizeHostData();
|
||||
return Classifier.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
|
||||
return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
|
||||
}
|
||||
|
||||
function getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications {
|
||||
synchronizeHostData();
|
||||
return Classifier.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
|
||||
return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
|
||||
}
|
||||
|
||||
function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
|
||||
// doesn't use compiler - no need to synchronize with host
|
||||
return Classifier.getSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
|
||||
return ts.getSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
|
||||
}
|
||||
|
||||
function getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications {
|
||||
// doesn't use compiler - no need to synchronize with host
|
||||
return Classifier.getEncodedSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
|
||||
return ts.getEncodedSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
|
||||
}
|
||||
|
||||
function getOutliningSpans(fileName: string): OutliningSpan[] {
|
||||
@@ -1913,6 +1773,7 @@ namespace ts {
|
||||
getEncodedSemanticClassifications,
|
||||
getCompletionsAtPosition,
|
||||
getCompletionEntryDetails,
|
||||
getCompletionEntrySymbol,
|
||||
getSignatureHelpItems,
|
||||
getQuickInfoAtPosition,
|
||||
getDefinitionAtPosition,
|
||||
|
||||
@@ -986,7 +986,7 @@ namespace ts {
|
||||
|
||||
constructor(factory: ShimFactory, private logger: Logger) {
|
||||
super(factory);
|
||||
this.classifier = Classifier.createClassifier();
|
||||
this.classifier = createClassifier();
|
||||
}
|
||||
|
||||
public getEncodedLexicalClassifications(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): string {
|
||||
@@ -1047,7 +1047,7 @@ namespace ts {
|
||||
`getPreProcessedFileInfo('${fileName}')`,
|
||||
() => {
|
||||
// for now treat files as JavaScript
|
||||
const result = PreProcess.preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
|
||||
const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
|
||||
return {
|
||||
referencedFiles: this.convertFileReferences(result.referencedFiles),
|
||||
importedFiles: this.convertFileReferences(result.importedFiles),
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"classifier.ts",
|
||||
"completions.ts",
|
||||
"documentHighlights.ts",
|
||||
"documentRegistry.ts",
|
||||
"findAllReferences.ts",
|
||||
"goToDefinition.ts",
|
||||
"jsDoc.ts",
|
||||
|
||||
+1
-93
@@ -194,6 +194,7 @@ namespace ts {
|
||||
|
||||
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo;
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol;
|
||||
|
||||
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo;
|
||||
|
||||
@@ -575,99 +576,6 @@ namespace ts {
|
||||
getEncodedLexicalClassifications(text: string, endOfLineState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
|
||||
* of files in the context.
|
||||
* SourceFile objects account for most of the memory usage by the language service. Sharing
|
||||
* the same DocumentRegistry instance between different instances of LanguageService allow
|
||||
* for more efficient memory utilization since all projects will share at least the library
|
||||
* file (lib.d.ts).
|
||||
*
|
||||
* A more advanced use of the document registry is to serialize sourceFile objects to disk
|
||||
* and re-hydrate them when needed.
|
||||
*
|
||||
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
|
||||
* to all subsequent createLanguageService calls.
|
||||
*/
|
||||
export interface DocumentRegistry {
|
||||
/**
|
||||
* Request a stored SourceFile with a given fileName and compilationSettings.
|
||||
* The first call to acquire will call createLanguageServiceSourceFile to generate
|
||||
* the SourceFile if was not found in the registry.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @parm scriptSnapshot Text of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
* @parm version Current version of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
*/
|
||||
acquireDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
acquireDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
/**
|
||||
* Request an updated version of an already existing SourceFile with a given fileName
|
||||
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
|
||||
* to get an updated SourceFile.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @param scriptSnapshot Text of the file.
|
||||
* @param version Current version of the file.
|
||||
*/
|
||||
updateDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
updateDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
|
||||
/**
|
||||
* Informs the DocumentRegistry that a file is not needed any longer.
|
||||
*
|
||||
* Note: It is not allowed to call release on a SourceFile that was not acquired from
|
||||
* this registry originally.
|
||||
*
|
||||
* @param fileName The name of the file to be released
|
||||
* @param compilationSettings The compilation settings used to acquire the file
|
||||
*/
|
||||
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
|
||||
|
||||
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
|
||||
|
||||
reportStats(): string;
|
||||
}
|
||||
|
||||
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
|
||||
|
||||
// TODO: move these to enums
|
||||
export namespace ScriptElementKind {
|
||||
export const unknown = "";
|
||||
|
||||
Reference in New Issue
Block a user