mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into dev/aozgaa/cSharpObjLiteralFormatting
This commit is contained in:
@@ -2362,6 +2362,10 @@ namespace ts {
|
||||
const symbolTable = hasModifier(thisContainer, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members;
|
||||
declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true);
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
// this.foo assignment in a source file
|
||||
// Do not bind. It would be nice to support this someday though.
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.fail(Debug.showSyntaxKind(thisContainer));
|
||||
|
||||
+35
-18
@@ -218,29 +218,40 @@ namespace ts {
|
||||
newProgram: Program;
|
||||
host: BuilderProgramHost;
|
||||
oldProgram: BuilderProgram | undefined;
|
||||
configFileParsingDiagnostics: ReadonlyArray<Diagnostic>;
|
||||
}
|
||||
|
||||
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, oldProgram?: BuilderProgram): BuilderCreationParameters {
|
||||
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderCreationParameters {
|
||||
let host: BuilderProgramHost;
|
||||
let newProgram: Program;
|
||||
if (isArray(newProgramOrRootNames)) {
|
||||
newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram());
|
||||
let oldProgram: BuilderProgram;
|
||||
if (newProgramOrRootNames === undefined) {
|
||||
Debug.assert(hostOrOptions === undefined);
|
||||
host = oldProgramOrHost as CompilerHost;
|
||||
oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
|
||||
Debug.assert(!!oldProgram);
|
||||
newProgram = oldProgram.getProgram();
|
||||
}
|
||||
else if (isArray(newProgramOrRootNames)) {
|
||||
oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
|
||||
newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram(), configFileParsingDiagnostics);
|
||||
host = oldProgramOrHost as CompilerHost;
|
||||
}
|
||||
else {
|
||||
newProgram = newProgramOrRootNames;
|
||||
host = hostOrOptions as BuilderProgramHost;
|
||||
oldProgram = oldProgramOrHost as BuilderProgram;
|
||||
configFileParsingDiagnostics = configFileParsingDiagnosticsOrOldProgram as ReadonlyArray<Diagnostic>;
|
||||
}
|
||||
return { host, newProgram, oldProgram };
|
||||
return { host, newProgram, oldProgram, configFileParsingDiagnostics: configFileParsingDiagnostics || emptyArray };
|
||||
}
|
||||
|
||||
export function createBuilderProgram(kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): SemanticDiagnosticsBuilderProgram;
|
||||
export function createBuilderProgram(kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): EmitAndSemanticDiagnosticsBuilderProgram;
|
||||
export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram }: BuilderCreationParameters) {
|
||||
export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram, configFileParsingDiagnostics }: BuilderCreationParameters) {
|
||||
// Return same program if underlying program doesnt change
|
||||
let oldState = oldProgram && oldProgram.getState();
|
||||
if (oldState && newProgram === oldState.program) {
|
||||
if (oldState && newProgram === oldState.program && configFileParsingDiagnostics !== newProgram.getConfigFileParsingDiagnostics()) {
|
||||
newProgram = undefined;
|
||||
oldState = undefined;
|
||||
return oldProgram;
|
||||
@@ -269,6 +280,7 @@ namespace ts {
|
||||
getSourceFiles: () => state.program.getSourceFiles(),
|
||||
getOptionsDiagnostics: cancellationToken => state.program.getOptionsDiagnostics(cancellationToken),
|
||||
getGlobalDiagnostics: cancellationToken => state.program.getGlobalDiagnostics(cancellationToken),
|
||||
getConfigFileParsingDiagnostics: () => configFileParsingDiagnostics || state.program.getConfigFileParsingDiagnostics(),
|
||||
getSyntacticDiagnostics: (sourceFile, cancellationToken) => state.program.getSyntacticDiagnostics(sourceFile, cancellationToken),
|
||||
getSemanticDiagnostics,
|
||||
emit,
|
||||
@@ -471,6 +483,10 @@ namespace ts {
|
||||
* Get the diagnostics that dont belong to any file
|
||||
*/
|
||||
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
/**
|
||||
* Get the diagnostics from config file parsing
|
||||
*/
|
||||
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
|
||||
/**
|
||||
* Get the syntax diagnostics, for all source files if source file is not supplied
|
||||
*/
|
||||
@@ -533,29 +549,29 @@ namespace ts {
|
||||
/**
|
||||
* Create the builder to manage semantic diagnostics and cache them
|
||||
*/
|
||||
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
|
||||
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
|
||||
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, oldProgram?: SemanticDiagnosticsBuilderProgram) {
|
||||
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram));
|
||||
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
|
||||
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
|
||||
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
|
||||
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the builder that can handle the changes in program and iterate through changed files
|
||||
* to emit the those files and manage semantic diagnostics cache as well
|
||||
*/
|
||||
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
|
||||
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
|
||||
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram) {
|
||||
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram));
|
||||
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
|
||||
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
|
||||
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
|
||||
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder thats just abstraction over program and can be used with watch
|
||||
*/
|
||||
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram): BuilderProgram;
|
||||
export function createAbstractBuilder(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram): BuilderProgram;
|
||||
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, oldProgram?: BuilderProgram): BuilderProgram {
|
||||
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram);
|
||||
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
|
||||
export function createAbstractBuilder(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
|
||||
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram {
|
||||
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics);
|
||||
return {
|
||||
// Only return program, all other methods are not implemented
|
||||
getProgram: () => program,
|
||||
@@ -565,6 +581,7 @@ namespace ts {
|
||||
getSourceFiles: notImplemented,
|
||||
getOptionsDiagnostics: notImplemented,
|
||||
getGlobalDiagnostics: notImplemented,
|
||||
getConfigFileParsingDiagnostics: notImplemented,
|
||||
getSyntacticDiagnostics: notImplemented,
|
||||
getSemanticDiagnostics: notImplemented,
|
||||
emit: notImplemented,
|
||||
|
||||
+122
-80
@@ -1234,7 +1234,7 @@ namespace ts {
|
||||
lastLocation.kind === SyntaxKind.Parameter ||
|
||||
(
|
||||
lastLocation === (<FunctionLikeDeclaration>location).type &&
|
||||
result.valueDeclaration.kind === SyntaxKind.Parameter
|
||||
!!findAncestor(result.valueDeclaration, isParameter)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1432,7 +1432,7 @@ namespace ts {
|
||||
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
|
||||
// If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
|
||||
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
|
||||
if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
|
||||
if (isUse && result && noUnusedIdentifiers && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
|
||||
result.isReferenced |= meaning;
|
||||
}
|
||||
|
||||
@@ -1741,8 +1741,8 @@ namespace ts {
|
||||
// Declaration files (and ambient modules)
|
||||
if (!file || file.isDeclarationFile) {
|
||||
// Definitely cannot have a synthetic default if they have a syntactic default member specified
|
||||
const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, dontResolveAlias);
|
||||
if (defaultExportSymbol && defaultExportSymbol.valueDeclaration && isSyntacticDefault(defaultExportSymbol.valueDeclaration)) {
|
||||
const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration
|
||||
if (defaultExportSymbol && some(defaultExportSymbol.declarations, isSyntacticDefault)) {
|
||||
return false;
|
||||
}
|
||||
// It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member
|
||||
@@ -1902,11 +1902,16 @@ namespace ts {
|
||||
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
|
||||
}
|
||||
|
||||
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
|
||||
return resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
|
||||
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol | undefined {
|
||||
const aliasLike = resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontResolveAlias);
|
||||
if (aliasLike) {
|
||||
return aliasLike;
|
||||
}
|
||||
checkExpression(node.expression);
|
||||
return getNodeLinks(node.expression).resolvedSymbol;
|
||||
}
|
||||
|
||||
function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol {
|
||||
function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol | undefined {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return getTargetOfImportEqualsDeclaration(<ImportEqualsDeclaration>node, dontRecursivelyResolve);
|
||||
@@ -2503,11 +2508,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
const id = "" + getSymbolId(symbol);
|
||||
let visitedSymbolTables: SymbolTable[];
|
||||
if (visitedSymbolTablesMap.has(id)) {
|
||||
visitedSymbolTables = visitedSymbolTablesMap.get(id);
|
||||
}
|
||||
else {
|
||||
let visitedSymbolTables = visitedSymbolTablesMap.get(id);
|
||||
if (!visitedSymbolTables) {
|
||||
visitedSymbolTablesMap.set(id, visitedSymbolTables = []);
|
||||
}
|
||||
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
|
||||
@@ -3324,6 +3326,10 @@ namespace ts {
|
||||
const methodDeclaration = <MethodSignature>signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context);
|
||||
methodDeclaration.name = propertyName;
|
||||
methodDeclaration.questionToken = optionalToken;
|
||||
if (propertySymbol.valueDeclaration) {
|
||||
// Copy comments to node for declaration emit
|
||||
setCommentRange(methodDeclaration, propertySymbol.valueDeclaration);
|
||||
}
|
||||
typeElements.push(methodDeclaration);
|
||||
}
|
||||
}
|
||||
@@ -3340,6 +3346,10 @@ namespace ts {
|
||||
optionalToken,
|
||||
propertyTypeNode,
|
||||
/*initializer*/ undefined);
|
||||
if (propertySymbol.valueDeclaration) {
|
||||
// Copy comments to node for declaration emit
|
||||
setCommentRange(propertySignature, propertySymbol.valueDeclaration);
|
||||
}
|
||||
typeElements.push(propertySignature);
|
||||
}
|
||||
}
|
||||
@@ -3393,7 +3403,8 @@ namespace ts {
|
||||
else {
|
||||
typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context));
|
||||
}
|
||||
const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context));
|
||||
|
||||
const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor));
|
||||
if (signature.thisParameter) {
|
||||
const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context);
|
||||
parameters.unshift(thisParameter);
|
||||
@@ -3433,7 +3444,7 @@ namespace ts {
|
||||
return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
|
||||
}
|
||||
|
||||
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration {
|
||||
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean): ParameterDeclaration {
|
||||
const parameterDeclaration = getDeclarationOfKind<ParameterDeclaration>(parameterSymbol, SyntaxKind.Parameter);
|
||||
Debug.assert(!!parameterDeclaration || isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter);
|
||||
|
||||
@@ -3443,7 +3454,7 @@ namespace ts {
|
||||
}
|
||||
const parameterTypeNode = typeToTypeNodeHelper(parameterType, context);
|
||||
|
||||
const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && parameterDeclaration && parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone);
|
||||
const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && preserveModifierFlags && parameterDeclaration && parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone);
|
||||
const dotDotDotToken = !parameterDeclaration || isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined;
|
||||
const name = parameterDeclaration
|
||||
? parameterDeclaration.name ?
|
||||
@@ -6233,7 +6244,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDefaultConstraintOfConditionalType(type: ConditionalType) {
|
||||
return getUnionType([getTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]);
|
||||
return getUnionType([getInferredTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]);
|
||||
}
|
||||
|
||||
function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type {
|
||||
@@ -8151,6 +8162,9 @@ namespace ts {
|
||||
}
|
||||
return indexInfo.type;
|
||||
}
|
||||
if (indexType.flags & TypeFlags.Never) {
|
||||
return neverType;
|
||||
}
|
||||
if (accessExpression && !isConstEnumObjectType(objectType)) {
|
||||
if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
|
||||
if (getIndexTypeOfType(objectType, IndexKind.Number)) {
|
||||
@@ -8340,16 +8354,19 @@ namespace ts {
|
||||
// If this is a distributive conditional type and the check type is generic we need to defer
|
||||
// resolution of the conditional type such that a later instantiation will properly distribute
|
||||
// over union types.
|
||||
if (!root.isDistributive || !maybeTypeOfKind(checkType, TypeFlags.Instantiable)) {
|
||||
const isDeferred = root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable);
|
||||
let combinedMapper: TypeMapper;
|
||||
if (root.inferTypeParameters) {
|
||||
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
|
||||
if (!isDeferred) {
|
||||
// We don't want inferences from constraints as they may cause us to eagerly resolve the
|
||||
// conditional type instead of deferring resolution. Also, we always want strict function
|
||||
// types rules (i.e. proper contravariance) for inferences.
|
||||
inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
|
||||
}
|
||||
combinedMapper = combineTypeMappers(mapper, context);
|
||||
}
|
||||
if (!isDeferred) {
|
||||
// Return union of trueType and falseType for 'any' since it matches anything
|
||||
if (checkType.flags & TypeFlags.Any) {
|
||||
return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]);
|
||||
@@ -8378,6 +8395,7 @@ namespace ts {
|
||||
result.checkType = erasedCheckType;
|
||||
result.extendsType = extendsType;
|
||||
result.mapper = mapper;
|
||||
result.combinedMapper = combinedMapper;
|
||||
result.aliasSymbol = root.aliasSymbol;
|
||||
result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper);
|
||||
return result;
|
||||
@@ -8391,6 +8409,12 @@ namespace ts {
|
||||
return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper));
|
||||
}
|
||||
|
||||
function getInferredTrueTypeFromConditionalType(type: ConditionalType) {
|
||||
return type.combinedMapper ?
|
||||
type.resolvedInferredTrueType || (type.resolvedInferredTrueType = instantiateType(type.root.trueType, type.combinedMapper)) :
|
||||
getTrueTypeFromConditionalType(type);
|
||||
}
|
||||
|
||||
function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] {
|
||||
let result: TypeParameter[];
|
||||
if (node.locals) {
|
||||
@@ -8900,7 +8924,7 @@ namespace ts {
|
||||
}
|
||||
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
|
||||
// also transient so that we can just store data on it directly.
|
||||
const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated);
|
||||
const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | (getCheckFlags(symbol) & CheckFlags.Late));
|
||||
result.declarations = symbol.declarations;
|
||||
result.parent = symbol.parent;
|
||||
result.target = symbol;
|
||||
@@ -14754,7 +14778,7 @@ namespace ts {
|
||||
return propsType;
|
||||
}
|
||||
|
||||
function getJsxPropsTypeFromClassType(hostClassType: Type, isJs: boolean, context: Node) {
|
||||
function getJsxPropsTypeFromClassType(hostClassType: Type, isJs: boolean, context: JsxOpeningLikeElement, reportErrors: boolean) {
|
||||
if (isTypeAny(hostClassType)) {
|
||||
return hostClassType;
|
||||
}
|
||||
@@ -14773,6 +14797,9 @@ namespace ts {
|
||||
|
||||
if (!attributesType) {
|
||||
// There is no property named 'props' on this instance type
|
||||
if (reportErrors && !!length(context.attributes.properties)) {
|
||||
error(context, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(propsName));
|
||||
}
|
||||
return emptyObjectType;
|
||||
}
|
||||
else if (isTypeAny(attributesType)) {
|
||||
@@ -14803,10 +14830,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getJsxPropsTypeFromConstructSignature(sig: Signature, isJs: boolean, context: Node) {
|
||||
function getJsxPropsTypeFromConstructSignature(sig: Signature, isJs: boolean, context: JsxOpeningLikeElement) {
|
||||
const hostClassType = getReturnTypeOfSignature(sig);
|
||||
if (hostClassType) {
|
||||
return getJsxPropsTypeFromClassType(hostClassType, isJs, context);
|
||||
return getJsxPropsTypeFromClassType(hostClassType, isJs, context, /*reportErrors*/ false);
|
||||
}
|
||||
return getJsxPropsTypeFromCallSignature(sig, context);
|
||||
}
|
||||
@@ -15844,7 +15871,7 @@ namespace ts {
|
||||
checkTypeRelatedTo(elemInstanceType, elementClassType, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
|
||||
}
|
||||
|
||||
return getJsxPropsTypeFromClassType(elemInstanceType, isInJavaScriptFile(openingLikeElement), openingLikeElement);
|
||||
return getJsxPropsTypeFromClassType(elemInstanceType, isInJavaScriptFile(openingLikeElement), openingLikeElement, /*reportErrors*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16049,28 +16076,21 @@ namespace ts {
|
||||
// attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes".
|
||||
const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement, checkMode);
|
||||
|
||||
// If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type.
|
||||
// but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass.
|
||||
if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || getPropertiesOfType(<ResolvedType>sourceAttributesType).length > 0)) {
|
||||
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(getJsxElementPropertiesName(getJsxNamespaceAt(openingLikeElement))));
|
||||
}
|
||||
else {
|
||||
// Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
|
||||
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
|
||||
// After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
|
||||
// This will allow excess properties in spread type as it is very common pattern to spread outer attributes into React component in its render method.
|
||||
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
|
||||
for (const attribute of openingLikeElement.attributes.properties) {
|
||||
if (!isJsxAttribute(attribute)) {
|
||||
continue;
|
||||
}
|
||||
const attrName = attribute.name;
|
||||
const isNotIgnoredJsxProperty = (isUnhyphenatedJsxName(idText(attrName)) || !!(getPropertyOfType(targetAttributesType, attrName.escapedText)));
|
||||
if (isNotIgnoredJsxProperty && !isKnownProperty(targetAttributesType, attrName.escapedText, /*isComparingJsxAttributes*/ true)) {
|
||||
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attrName), typeToString(targetAttributesType));
|
||||
// We break here so that errors won't be cascading
|
||||
break;
|
||||
}
|
||||
// Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
|
||||
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
|
||||
// After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
|
||||
// This will allow excess properties in spread type as it is very common pattern to spread outer attributes into React component in its render method.
|
||||
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
|
||||
for (const attribute of openingLikeElement.attributes.properties) {
|
||||
if (!isJsxAttribute(attribute)) {
|
||||
continue;
|
||||
}
|
||||
const attrName = attribute.name;
|
||||
const isNotIgnoredJsxProperty = (isUnhyphenatedJsxName(idText(attrName)) || !!(getPropertyOfType(targetAttributesType, attrName.escapedText)));
|
||||
if (isNotIgnoredJsxProperty && !isKnownProperty(targetAttributesType, attrName.escapedText, /*isComparingJsxAttributes*/ true)) {
|
||||
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attrName), typeToString(targetAttributesType));
|
||||
// We break here so that errors won't be cascading
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16100,7 +16120,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isMethodLike(symbol: Symbol) {
|
||||
return !!(symbol.flags & SymbolFlags.Method || getCheckFlags(symbol) & CheckFlags.SyntheticMethod);
|
||||
return !!(symbol.flags & SymbolFlags.Method ||
|
||||
getCheckFlags(symbol) & CheckFlags.SyntheticMethod ||
|
||||
isInJavaScriptFile(symbol.valueDeclaration) && isFunctionLikeDeclaration(getAssignedJavascriptInitializer(symbol.valueDeclaration)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19153,6 +19175,7 @@ namespace ts {
|
||||
|
||||
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type): Type {
|
||||
const properties = node.properties;
|
||||
checkGrammarForDisallowedTrailingComma(properties, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma);
|
||||
if (strictNullChecks && properties.length === 0) {
|
||||
return checkNonNullType(sourceType, node);
|
||||
}
|
||||
@@ -19211,6 +19234,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type {
|
||||
const elements = node.elements;
|
||||
checkGrammarForDisallowedTrailingComma(elements, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma);
|
||||
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
|
||||
checkExternalEmitHelpers(node, ExternalEmitHelpers.Read);
|
||||
}
|
||||
@@ -19219,7 +19244,6 @@ namespace ts {
|
||||
// present (aka the tuple element property). This call also checks that the parentType is in
|
||||
// fact an iterable or array (depending on target language).
|
||||
const elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || unknownType;
|
||||
const elements = node.elements;
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode);
|
||||
}
|
||||
@@ -22486,19 +22510,17 @@ namespace ts {
|
||||
function checkForOfStatement(node: ForOfStatement): void {
|
||||
checkGrammarForInOrForOfStatement(node);
|
||||
|
||||
if (node.kind === SyntaxKind.ForOfStatement) {
|
||||
if (node.awaitModifier) {
|
||||
const functionFlags = getFunctionFlags(getContainingFunction(node));
|
||||
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
|
||||
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
|
||||
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
|
||||
}
|
||||
}
|
||||
else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) {
|
||||
// for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled
|
||||
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes);
|
||||
if (node.awaitModifier) {
|
||||
const functionFlags = getFunctionFlags(getContainingFunction(node));
|
||||
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
|
||||
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
|
||||
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
|
||||
}
|
||||
}
|
||||
else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) {
|
||||
// for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled
|
||||
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes);
|
||||
}
|
||||
|
||||
// Check the LHS and RHS
|
||||
// If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS
|
||||
@@ -22577,8 +22599,8 @@ namespace ts {
|
||||
|
||||
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
|
||||
// in this case error about missing name is already reported - do not report extra one
|
||||
if (!isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive)) {
|
||||
error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter);
|
||||
if (rightType === neverType || !isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive)) {
|
||||
error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter_but_here_has_type_0, typeToString(rightType));
|
||||
}
|
||||
|
||||
checkSourceElement(node.statement);
|
||||
@@ -22614,7 +22636,12 @@ namespace ts {
|
||||
* we want to get the iterated type of an iterable for ES2015 or later, or the iterated type
|
||||
* of a iterable (if defined globally) or element type of an array like for ES2015 or earlier.
|
||||
*/
|
||||
function getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, allowAsyncIterables: boolean, checkAssignability: boolean): Type {
|
||||
function getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, allowAsyncIterables: boolean, checkAssignability: boolean): Type | undefined {
|
||||
if (inputType === neverType) {
|
||||
reportTypeNotIterableError(errorNode, inputType, allowAsyncIterables);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const uplevelIteration = languageVersion >= ScriptTarget.ES2015;
|
||||
const downlevelIteration = !uplevelIteration && compilerOptions.downlevelIteration;
|
||||
|
||||
@@ -22674,13 +22701,18 @@ namespace ts {
|
||||
// want to say that number is not an array type. But if the input was just
|
||||
// number and string input is allowed, we want to say that number is not an
|
||||
// array type or a string type.
|
||||
const isIterable = !!getIteratedTypeOfIterable(inputType, /* errorNode */ undefined, allowAsyncIterables, /*allowSyncIterables*/ true, checkAssignability);
|
||||
const diagnostic = !allowStringInput || hasStringConstituent
|
||||
? downlevelIteration
|
||||
? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
|
||||
: Diagnostics.Type_0_is_not_an_array_type
|
||||
: isIterable
|
||||
? Diagnostics.Type_0_is_not_an_array_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators
|
||||
: Diagnostics.Type_0_is_not_an_array_type
|
||||
: downlevelIteration
|
||||
? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
|
||||
: Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
|
||||
: isIterable
|
||||
? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators
|
||||
: Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
|
||||
error(errorNode, diagnostic, typeToString(arrayType));
|
||||
}
|
||||
return hasStringConstituent ? stringType : undefined;
|
||||
@@ -22781,11 +22813,8 @@ namespace ts {
|
||||
const signatures = methodType && getSignaturesOfType(methodType, SignatureKind.Call);
|
||||
if (!some(signatures)) {
|
||||
if (errorNode) {
|
||||
error(errorNode,
|
||||
allowAsyncIterables
|
||||
? Diagnostics.Type_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator
|
||||
: Diagnostics.Type_must_have_a_Symbol_iterator_method_that_returns_an_iterator);
|
||||
// only report on the first error
|
||||
reportTypeNotIterableError(errorNode, type, allowAsyncIterables);
|
||||
errorNode = undefined;
|
||||
}
|
||||
return undefined;
|
||||
@@ -22808,6 +22837,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function reportTypeNotIterableError(errorNode: Node, type: Type, allowAsyncIterables: boolean): void {
|
||||
error(errorNode, allowAsyncIterables
|
||||
? Diagnostics.Type_0_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator
|
||||
: Diagnostics.Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator, typeToString(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function has very similar logic as getIteratedTypeOfIterable, except that it operates on
|
||||
* Iterators instead of Iterables. Here is the structure:
|
||||
@@ -25030,12 +25065,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (entityName.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(entityName)) {
|
||||
return resolveEntityName(entityName,
|
||||
/*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
|
||||
// Even an entity name expression that doesn't resolve as an entityname may still typecheck as a property access expression
|
||||
const success = resolveEntityName(entityName,
|
||||
/*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*ignoreErrors*/ true);
|
||||
if (success && success !== unknownSymbol) {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
if (entityName.kind !== SyntaxKind.PropertyAccessExpression && isInRightSideOfImportOrExportAssignment(entityName)) {
|
||||
// Since we already checked for ExportAssignment, this really could only be an Import
|
||||
else if (entityName.kind !== SyntaxKind.PropertyAccessExpression && isInRightSideOfImportOrExportAssignment(entityName)) {
|
||||
const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration);
|
||||
Debug.assert(importEqualsDeclaration !== undefined);
|
||||
return getSymbolOfPartOfRightHandSideOfImportEquals(entityName, /*dontResolveAlias*/ true);
|
||||
@@ -25897,7 +25934,7 @@ namespace ts {
|
||||
// populate reverse mapping: file path -> type reference directive that was resolved to this file
|
||||
fileToDirective = createMap<string>();
|
||||
resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => {
|
||||
if (!resolvedDirective) {
|
||||
if (!resolvedDirective || !resolvedDirective.resolvedFileName) {
|
||||
return;
|
||||
}
|
||||
const file = host.getSourceFile(resolvedDirective.resolvedFileName);
|
||||
@@ -25957,18 +25994,23 @@ namespace ts {
|
||||
getJsxFactoryEntity: location => location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity
|
||||
};
|
||||
|
||||
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {
|
||||
return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && node.parent.parent && node.parent.parent.kind === SyntaxKind.HeritageClause;
|
||||
}
|
||||
|
||||
// defined here to avoid outer scope pollution
|
||||
function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] {
|
||||
// program does not have any files with type reference directives - bail out
|
||||
if (!fileToDirective) {
|
||||
return undefined;
|
||||
}
|
||||
// property access can only be used as values
|
||||
// property access can only be used as values, or types when within an expression with type arguments inside a heritage clause
|
||||
// qualified names can only be used as types\namespaces
|
||||
// identifiers are treated as values only if they appear in type queries
|
||||
const meaning = (node.kind === SyntaxKind.PropertyAccessExpression) || (node.kind === SyntaxKind.Identifier && isInTypeQuery(node))
|
||||
? SymbolFlags.Value | SymbolFlags.ExportValue
|
||||
: SymbolFlags.Type | SymbolFlags.Namespace;
|
||||
let meaning = SymbolFlags.Type | SymbolFlags.Namespace;
|
||||
if ((node.kind === SyntaxKind.Identifier && isInTypeQuery(node)) || (node.kind === SyntaxKind.PropertyAccessExpression && !isInHeritageClause(node))) {
|
||||
meaning = SymbolFlags.Value | SymbolFlags.ExportValue;
|
||||
}
|
||||
|
||||
const symbol = resolveEntityName(node, meaning, /*ignoreErrors*/ true);
|
||||
return symbol && symbol !== unknownSymbol ? getTypeReferenceDirectivesForSymbol(symbol, meaning) : undefined;
|
||||
@@ -26483,11 +26525,9 @@ namespace ts {
|
||||
return grammarErrorOnNode(asyncModifier, Diagnostics._0_modifier_cannot_be_used_here, "async");
|
||||
}
|
||||
|
||||
function checkGrammarForDisallowedTrailingComma(list: NodeArray<Node>): boolean {
|
||||
function checkGrammarForDisallowedTrailingComma(list: NodeArray<Node>, diag = Diagnostics.Trailing_comma_not_allowed): boolean {
|
||||
if (list && list.hasTrailingComma) {
|
||||
const start = list.end - ",".length;
|
||||
const end = list.end;
|
||||
return grammarErrorAtPos(list[0], start, end - start, Diagnostics.Trailing_comma_not_allowed);
|
||||
return grammarErrorAtPos(list[0], list.end - ",".length, ",".length, diag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26509,6 +26549,7 @@ namespace ts {
|
||||
if (i !== (parameterCount - 1)) {
|
||||
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list);
|
||||
}
|
||||
checkGrammarForDisallowedTrailingComma(parameters, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma);
|
||||
|
||||
if (isBindingPattern(parameter.name)) {
|
||||
return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
|
||||
@@ -27123,6 +27164,7 @@ namespace ts {
|
||||
if (node !== last(elements)) {
|
||||
return grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern);
|
||||
}
|
||||
checkGrammarForDisallowedTrailingComma(elements, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma);
|
||||
|
||||
if (node.name.kind === SyntaxKind.ArrayBindingPattern || node.name.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
|
||||
|
||||
@@ -1570,10 +1570,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function formatStringFromArgs(text: string, args: { [index: number]: string; }, baseIndex?: number): string {
|
||||
export function formatStringFromArgs(text: string, args: ArrayLike<string>, baseIndex?: number): string {
|
||||
baseIndex = baseIndex || 0;
|
||||
|
||||
return text.replace(/{(\d+)}/g, (_match, index?) => args[+index + baseIndex]);
|
||||
return text.replace(/{(\d+)}/g, (_match, index?: string) => Debug.assertDefined(args[+index + baseIndex]));
|
||||
}
|
||||
|
||||
export let localizedDiagnosticMessages: MapLike<string>;
|
||||
@@ -2909,7 +2909,7 @@ namespace ts {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message: string): A {
|
||||
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
|
||||
for (const v of value) {
|
||||
assertDefined(v, message);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
"category": "Error",
|
||||
"code": 1012
|
||||
},
|
||||
"A rest parameter or binding pattern may not have a trailing comma.": {
|
||||
"category": "Error",
|
||||
"code": 1013
|
||||
},
|
||||
"A rest parameter must be last in a parameter list.": {
|
||||
"category": "Error",
|
||||
"code": 1014
|
||||
@@ -1372,7 +1376,7 @@
|
||||
"category": "Error",
|
||||
"code": 2406
|
||||
},
|
||||
"The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter.": {
|
||||
"The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2407
|
||||
},
|
||||
@@ -1668,7 +1672,7 @@
|
||||
"category": "Error",
|
||||
"code": 2487
|
||||
},
|
||||
"Type must have a '[Symbol.iterator]()' method that returns an iterator.": {
|
||||
"Type '{0}' must have a '[Symbol.iterator]()' method that returns an iterator.": {
|
||||
"category": "Error",
|
||||
"code": 2488
|
||||
},
|
||||
@@ -1732,7 +1736,7 @@
|
||||
"category": "Error",
|
||||
"code": 2503
|
||||
},
|
||||
"Type must have a '[Symbol.asyncIterator]()' method that returns an async iterator.": {
|
||||
"Type '{0}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.": {
|
||||
"category": "Error",
|
||||
"code": 2504
|
||||
},
|
||||
@@ -1988,6 +1992,14 @@
|
||||
"category": "Error",
|
||||
"code": 2567
|
||||
},
|
||||
"Type '{0}' is not an array type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": {
|
||||
"category": "Error",
|
||||
"code": 2568
|
||||
},
|
||||
"Type '{0}' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": {
|
||||
"category": "Error",
|
||||
"code": 2569
|
||||
},
|
||||
|
||||
"JSX element attributes type '{0}' may not be a union type.": {
|
||||
"category": "Error",
|
||||
@@ -4026,5 +4038,101 @@
|
||||
"Add definite assignment assertion to property '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 95020
|
||||
},
|
||||
"Add all missing members": {
|
||||
"category": "Message",
|
||||
"code": 95022
|
||||
},
|
||||
"Infer all types from usage": {
|
||||
"category": "Message",
|
||||
"code": 95023
|
||||
},
|
||||
"Delete all unused declarations": {
|
||||
"category": "Message",
|
||||
"code": 95024
|
||||
},
|
||||
"Prefix all unused declarations with '_' where possible": {
|
||||
"category": "Message",
|
||||
"code": 95025
|
||||
},
|
||||
"Fix all detected spelling errors": {
|
||||
"category": "Message",
|
||||
"code": 95026
|
||||
},
|
||||
"Add initializers to all uninitialized properties": {
|
||||
"category": "Message",
|
||||
"code": 95027
|
||||
},
|
||||
"Add definite assignment assertions to all uninitialized properties": {
|
||||
"category": "Message",
|
||||
"code": 95028
|
||||
},
|
||||
"Add undefined type to all uninitialized properties": {
|
||||
"category": "Message",
|
||||
"code": 95029
|
||||
},
|
||||
"Change all jsdoc-style types to TypeScript": {
|
||||
"category": "Message",
|
||||
"code": 95030
|
||||
},
|
||||
"Change all jsdoc-style types to TypeScript (and add '| undefined' to nullable types)": {
|
||||
"category": "Message",
|
||||
"code": 95031
|
||||
},
|
||||
"Implement all unimplemented interfaces": {
|
||||
"category": "Message",
|
||||
"code": 95032
|
||||
},
|
||||
"Install all missing types packages": {
|
||||
"category": "Message",
|
||||
"code": 95033
|
||||
},
|
||||
"Rewrite all as indexed access types": {
|
||||
"category": "Message",
|
||||
"code": 95034
|
||||
},
|
||||
"Convert all to default imports": {
|
||||
"category": "Message",
|
||||
"code": 95035
|
||||
},
|
||||
"Make all 'super()' calls the first statement in their constructor": {
|
||||
"category": "Message",
|
||||
"code": 95036
|
||||
},
|
||||
"Add 'this.' to all unresolved variables matching a member name": {
|
||||
"category": "Message",
|
||||
"code": 95037
|
||||
},
|
||||
"Change all extended interfaces to 'implements'": {
|
||||
"category": "Message",
|
||||
"code": 95038
|
||||
},
|
||||
"Add all missing super calls": {
|
||||
"category": "Message",
|
||||
"code": 95039
|
||||
},
|
||||
"Implement all inherited abstract classes": {
|
||||
"category": "Message",
|
||||
"code": 95040
|
||||
},
|
||||
"Add all missing 'async' modifiers": {
|
||||
"category": "Message",
|
||||
"code": 95041
|
||||
},
|
||||
"Add '@ts-ignore' to all error messages": {
|
||||
"category": "Message",
|
||||
"code": 95042
|
||||
},
|
||||
"Annotate everything with types from JSDoc": {
|
||||
"category": "Message",
|
||||
"code": 95043
|
||||
},
|
||||
"Add '()' to all uncalled decorators": {
|
||||
"category": "Message",
|
||||
"code": 95044
|
||||
},
|
||||
"Convert all constructor functions to classes": {
|
||||
"category": "Message",
|
||||
"code": 95045
|
||||
}
|
||||
}
|
||||
|
||||
+31
-12
@@ -568,7 +568,7 @@ namespace ts {
|
||||
writeSpace();
|
||||
writeKeyword("in");
|
||||
writeSpace();
|
||||
emit(node.constraint);
|
||||
emitIfPresent(node.constraint);
|
||||
}
|
||||
|
||||
function pipelineEmitUnspecified(node: Node): void {
|
||||
@@ -1046,7 +1046,7 @@ namespace ts {
|
||||
}
|
||||
emitIfPresent(node.questionToken);
|
||||
if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) {
|
||||
emit(node.type);
|
||||
emitIfPresent(node.type);
|
||||
}
|
||||
else {
|
||||
emitTypeAnnotation(node.type);
|
||||
@@ -1174,14 +1174,14 @@ namespace ts {
|
||||
writeSpace();
|
||||
writePunctuation("=>");
|
||||
writeSpace();
|
||||
emit(node.type);
|
||||
emitIfPresent(node.type);
|
||||
}
|
||||
|
||||
function emitJSDocFunctionType(node: JSDocFunctionType) {
|
||||
write("function");
|
||||
emitParameters(node, node.parameters);
|
||||
write(":");
|
||||
emit(node.type);
|
||||
emitIfPresent(node.type);
|
||||
}
|
||||
|
||||
|
||||
@@ -1208,7 +1208,7 @@ namespace ts {
|
||||
writeSpace();
|
||||
writePunctuation("=>");
|
||||
writeSpace();
|
||||
emit(node.type);
|
||||
emitIfPresent(node.type);
|
||||
}
|
||||
|
||||
function emitTypeQuery(node: TypeQueryNode) {
|
||||
@@ -1322,7 +1322,7 @@ namespace ts {
|
||||
}
|
||||
writePunctuation(":");
|
||||
writeSpace();
|
||||
emit(node.type);
|
||||
emitIfPresent(node.type);
|
||||
writeSemicolon();
|
||||
if (emitFlags & EmitFlags.SingleLine) {
|
||||
writeSpace();
|
||||
@@ -1593,7 +1593,7 @@ namespace ts {
|
||||
|
||||
function emitYieldExpression(node: YieldExpression) {
|
||||
emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node);
|
||||
emit(node.asteriskToken);
|
||||
emitIfPresent(node.asteriskToken);
|
||||
emitExpressionWithLeadingSpace(node.expression);
|
||||
}
|
||||
|
||||
@@ -2156,12 +2156,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function emitImportClause(node: ImportClause) {
|
||||
emit(node.name);
|
||||
emitIfPresent(node.name);
|
||||
if (node.name && node.namedBindings) {
|
||||
emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node);
|
||||
writeSpace();
|
||||
}
|
||||
emit(node.namedBindings);
|
||||
emitIfPresent(node.namedBindings);
|
||||
}
|
||||
|
||||
function emitNamespaceImport(node: NamespaceImport) {
|
||||
@@ -2473,14 +2473,33 @@ namespace ts {
|
||||
}
|
||||
|
||||
function emitSyntheticTripleSlashReferencesIfNeeded(node: Bundle) {
|
||||
emitTripleSlashDirectives(node.syntheticFileReferences || [], node.syntheticTypeReferences || []);
|
||||
emitTripleSlashDirectives(node.hasNoDefaultLib, node.syntheticFileReferences || [], node.syntheticTypeReferences || []);
|
||||
}
|
||||
|
||||
function emitTripleSlashDirectivesIfNeeded(node: SourceFile) {
|
||||
if (node.isDeclarationFile) emitTripleSlashDirectives(node.referencedFiles, node.typeReferenceDirectives);
|
||||
if (node.isDeclarationFile) emitTripleSlashDirectives(node.hasNoDefaultLib, node.referencedFiles, node.typeReferenceDirectives);
|
||||
}
|
||||
|
||||
function emitTripleSlashDirectives(files: ReadonlyArray<FileReference>, types: ReadonlyArray<FileReference>) {
|
||||
function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: ReadonlyArray<FileReference>, types: ReadonlyArray<FileReference>) {
|
||||
if (hasNoDefaultLib) {
|
||||
write(`/// <reference no-default-lib="true"/>`);
|
||||
writeLine();
|
||||
}
|
||||
if (currentSourceFile && currentSourceFile.moduleName) {
|
||||
write(`/// <amd-module name="${currentSourceFile.moduleName}" />`);
|
||||
writeLine();
|
||||
}
|
||||
if (currentSourceFile && currentSourceFile.amdDependencies) {
|
||||
for (const dep of currentSourceFile.amdDependencies) {
|
||||
if (dep.name) {
|
||||
write(`/// <amd-dependency name="${dep.name}" path="${dep.path}" />`);
|
||||
}
|
||||
else {
|
||||
write(`/// <amd-dependency path="${dep.path}" />`);
|
||||
}
|
||||
writeLine();
|
||||
}
|
||||
}
|
||||
for (const directive of files) {
|
||||
write(`/// <reference path="${directive.fileName}" />`);
|
||||
writeLine();
|
||||
|
||||
@@ -2387,12 +2387,13 @@ namespace ts {
|
||||
|
||||
// Top-level nodes
|
||||
|
||||
export function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray<Statement>, isDeclarationFile?: boolean, referencedFiles?: SourceFile["referencedFiles"], typeReferences?: SourceFile["typeReferenceDirectives"]) {
|
||||
export function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray<Statement>, isDeclarationFile?: boolean, referencedFiles?: SourceFile["referencedFiles"], typeReferences?: SourceFile["typeReferenceDirectives"], hasNoDefaultLib?: boolean) {
|
||||
if (
|
||||
node.statements !== statements ||
|
||||
(isDeclarationFile !== undefined && node.isDeclarationFile !== isDeclarationFile) ||
|
||||
(referencedFiles !== undefined && node.referencedFiles !== referencedFiles) ||
|
||||
(typeReferences !== undefined && node.typeReferenceDirectives !== typeReferences)
|
||||
(typeReferences !== undefined && node.typeReferenceDirectives !== typeReferences) ||
|
||||
(hasNoDefaultLib !== undefined && node.hasNoDefaultLib !== hasNoDefaultLib)
|
||||
) {
|
||||
const updated = <SourceFile>createSynthesizedNode(SyntaxKind.SourceFile);
|
||||
updated.flags |= node.flags;
|
||||
@@ -2404,11 +2405,11 @@ namespace ts {
|
||||
updated.isDeclarationFile = isDeclarationFile === undefined ? node.isDeclarationFile : isDeclarationFile;
|
||||
updated.referencedFiles = referencedFiles === undefined ? node.referencedFiles : referencedFiles;
|
||||
updated.typeReferenceDirectives = typeReferences === undefined ? node.typeReferenceDirectives : typeReferences;
|
||||
updated.hasNoDefaultLib = hasNoDefaultLib === undefined ? node.hasNoDefaultLib : hasNoDefaultLib;
|
||||
if (node.amdDependencies !== undefined) updated.amdDependencies = node.amdDependencies;
|
||||
if (node.moduleName !== undefined) updated.moduleName = node.moduleName;
|
||||
if (node.languageVariant !== undefined) updated.languageVariant = node.languageVariant;
|
||||
if (node.renamedDependencies !== undefined) updated.renamedDependencies = node.renamedDependencies;
|
||||
if (node.hasNoDefaultLib !== undefined) updated.hasNoDefaultLib = node.hasNoDefaultLib;
|
||||
if (node.languageVersion !== undefined) updated.languageVersion = node.languageVersion;
|
||||
if (node.scriptKind !== undefined) updated.scriptKind = node.scriptKind;
|
||||
if (node.externalModuleIndicator !== undefined) updated.externalModuleIndicator = node.externalModuleIndicator;
|
||||
|
||||
@@ -794,6 +794,7 @@ namespace ts {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, path, real);
|
||||
}
|
||||
Debug.assert(host.fileExists(real), `${path} linked to nonexistent file ${real}`); // tslint:disable-line
|
||||
return real;
|
||||
}
|
||||
|
||||
|
||||
+49
-93
@@ -1344,26 +1344,26 @@ namespace ts {
|
||||
}
|
||||
|
||||
function nextTokenCanFollowModifier() {
|
||||
if (token() === SyntaxKind.ConstKeyword) {
|
||||
// 'const' is only a modifier if followed by 'enum'.
|
||||
return nextToken() === SyntaxKind.EnumKeyword;
|
||||
switch (token()) {
|
||||
case SyntaxKind.ConstKeyword:
|
||||
// 'const' is only a modifier if followed by 'enum'.
|
||||
return nextToken() === SyntaxKind.EnumKeyword;
|
||||
case SyntaxKind.ExportKeyword:
|
||||
nextToken();
|
||||
if (token() === SyntaxKind.DefaultKeyword) {
|
||||
return lookAhead(nextTokenCanFollowDefaultKeyword);
|
||||
}
|
||||
return token() !== SyntaxKind.AsteriskToken && token() !== SyntaxKind.AsKeyword && token() !== SyntaxKind.OpenBraceToken && canFollowModifier();
|
||||
case SyntaxKind.DefaultKeyword:
|
||||
return nextTokenCanFollowDefaultKeyword();
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
nextToken();
|
||||
return canFollowModifier();
|
||||
default:
|
||||
return nextTokenIsOnSameLineAndCanFollowModifier();
|
||||
}
|
||||
if (token() === SyntaxKind.ExportKeyword) {
|
||||
nextToken();
|
||||
if (token() === SyntaxKind.DefaultKeyword) {
|
||||
return lookAhead(nextTokenCanFollowDefaultKeyword);
|
||||
}
|
||||
return token() !== SyntaxKind.AsteriskToken && token() !== SyntaxKind.AsKeyword && token() !== SyntaxKind.OpenBraceToken && canFollowModifier();
|
||||
}
|
||||
if (token() === SyntaxKind.DefaultKeyword) {
|
||||
return nextTokenCanFollowDefaultKeyword();
|
||||
}
|
||||
if (token() === SyntaxKind.StaticKeyword) {
|
||||
nextToken();
|
||||
return canFollowModifier();
|
||||
}
|
||||
|
||||
return nextTokenIsOnSameLineAndCanFollowModifier();
|
||||
}
|
||||
|
||||
function parseAnyContextualModifier(): boolean {
|
||||
@@ -3600,7 +3600,7 @@ namespace ts {
|
||||
// reScanGreaterToken so that we merge token sequences like > and = into >=
|
||||
|
||||
reScanGreaterToken();
|
||||
const newPrecedence = getBinaryOperatorPrecedence();
|
||||
const newPrecedence = getBinaryOperatorPrecedence(token());
|
||||
|
||||
// Check the precedence to see if we should "take" this operator
|
||||
// - For left associative operator (all operator but **), consume the operator,
|
||||
@@ -3662,52 +3662,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getBinaryOperatorPrecedence() > 0;
|
||||
}
|
||||
|
||||
function getBinaryOperatorPrecedence(): number {
|
||||
switch (token()) {
|
||||
case SyntaxKind.BarBarToken:
|
||||
return 1;
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
return 2;
|
||||
case SyntaxKind.BarToken:
|
||||
return 3;
|
||||
case SyntaxKind.CaretToken:
|
||||
return 4;
|
||||
case SyntaxKind.AmpersandToken:
|
||||
return 5;
|
||||
case SyntaxKind.EqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
return 6;
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
case SyntaxKind.InKeyword:
|
||||
case SyntaxKind.AsKeyword:
|
||||
return 7;
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
return 8;
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
return 9;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
return 10;
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
return 11;
|
||||
}
|
||||
|
||||
// -1 is lower than all other precedences. Returning it will cause binary expression
|
||||
// parsing to stop.
|
||||
return -1;
|
||||
return getBinaryOperatorPrecedence(token()) > 0;
|
||||
}
|
||||
|
||||
function makeBinaryExpression(left: Expression, operatorToken: BinaryOperatorToken, right: Expression): BinaryExpression {
|
||||
@@ -3795,7 +3750,7 @@ namespace ts {
|
||||
if (isUpdateExpression()) {
|
||||
const updateExpression = parseUpdateExpression();
|
||||
return token() === SyntaxKind.AsteriskAsteriskToken ?
|
||||
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), updateExpression) :
|
||||
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(token()), updateExpression) :
|
||||
updateExpression;
|
||||
}
|
||||
|
||||
@@ -5506,19 +5461,6 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function isClassMemberModifier(idToken: SyntaxKind) {
|
||||
switch (idToken) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isClassMemberStart(): boolean {
|
||||
let idToken: SyntaxKind;
|
||||
|
||||
@@ -6555,7 +6497,7 @@ namespace ts {
|
||||
const result = target === PropertyLikeParse.Parameter ?
|
||||
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
|
||||
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
|
||||
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name);
|
||||
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name, target);
|
||||
if (nestedTypeLiteral) {
|
||||
typeExpression = nestedTypeLiteral;
|
||||
isNameFirst = true;
|
||||
@@ -6569,15 +6511,17 @@ namespace ts {
|
||||
return finishNode(result);
|
||||
}
|
||||
|
||||
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName) {
|
||||
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName, target: PropertyLikeParse) {
|
||||
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
|
||||
const typeLiteralExpression = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos());
|
||||
let child: JSDocParameterTag | false;
|
||||
let child: JSDocPropertyLikeTag | JSDocTypeTag | false;
|
||||
let jsdocTypeLiteral: JSDocTypeLiteral;
|
||||
const start = scanner.getStartPos();
|
||||
let children: JSDocParameterTag[];
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter, name))) {
|
||||
children = append(children, child);
|
||||
let children: JSDocPropertyLikeTag[];
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(target, name))) {
|
||||
if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) {
|
||||
children = append(children, child);
|
||||
}
|
||||
}
|
||||
if (children) {
|
||||
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
|
||||
@@ -6681,7 +6625,7 @@ namespace ts {
|
||||
let jsdocTypeLiteral: JSDocTypeLiteral;
|
||||
let childTypeTag: JSDocTypeTag;
|
||||
const start = scanner.getStartPos();
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) {
|
||||
while (child = tryParse(() => parseChildPropertyTag())) {
|
||||
if (!jsdocTypeLiteral) {
|
||||
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
|
||||
}
|
||||
@@ -6741,8 +6685,10 @@ namespace ts {
|
||||
return a.escapedText === b.escapedText;
|
||||
}
|
||||
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Property): JSDocTypeTag | JSDocPropertyTag | false;
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Parameter, name: EntityName): JSDocParameterTag | false;
|
||||
function parseChildPropertyTag() {
|
||||
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property) as JSDocTypeTag | JSDocPropertyTag | false;
|
||||
}
|
||||
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
|
||||
let canParseTag = true;
|
||||
let seenAsterisk = false;
|
||||
@@ -7023,7 +6969,7 @@ namespace ts {
|
||||
forEachChild(node, visitNode, visitArray);
|
||||
if (hasJSDocNodes(node)) {
|
||||
for (const jsDocComment of node.jsDoc) {
|
||||
forEachChild(jsDocComment, visitNode, visitArray);
|
||||
visitNode(<IncrementalNode><Node>jsDocComment);
|
||||
}
|
||||
}
|
||||
checkNodePositions(node, aggressiveChecks);
|
||||
@@ -7129,10 +7075,16 @@ namespace ts {
|
||||
function checkNodePositions(node: Node, aggressiveChecks: boolean) {
|
||||
if (aggressiveChecks) {
|
||||
let pos = node.pos;
|
||||
forEachChild(node, child => {
|
||||
const visitNode = (child: Node) => {
|
||||
Debug.assert(child.pos >= pos);
|
||||
pos = child.end;
|
||||
});
|
||||
};
|
||||
if (hasJSDocNodes(node)) {
|
||||
for (const jsDocComment of node.jsDoc) {
|
||||
visitNode(jsDocComment);
|
||||
}
|
||||
}
|
||||
forEachChild(node, visitNode);
|
||||
Debug.assert(pos <= node.end);
|
||||
}
|
||||
}
|
||||
@@ -7170,7 +7122,11 @@ namespace ts {
|
||||
// Adjust the pos or end (or both) of the intersecting element accordingly.
|
||||
adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
|
||||
forEachChild(child, visitNode, visitArray);
|
||||
|
||||
if (hasJSDocNodes(child)) {
|
||||
for (const jsDocComment of child.jsDoc) {
|
||||
visitNode(<IncrementalNode><Node>jsDocComment);
|
||||
}
|
||||
}
|
||||
checkNodePositions(child, aggressiveChecks);
|
||||
return;
|
||||
}
|
||||
|
||||
+19
-11
@@ -198,6 +198,7 @@ namespace ts {
|
||||
|
||||
export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[] {
|
||||
const diagnostics = [
|
||||
...program.getConfigFileParsingDiagnostics(),
|
||||
...program.getOptionsDiagnostics(cancellationToken),
|
||||
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
|
||||
...program.getGlobalDiagnostics(cancellationToken),
|
||||
@@ -454,6 +455,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray<Diagnostic> {
|
||||
return configFileParseResult.options.configFile ?
|
||||
configFileParseResult.options.configFile.parseDiagnostics.concat(configFileParseResult.errors) :
|
||||
configFileParseResult.errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determined if source file needs to be re-created even if its text hasn't changed
|
||||
*/
|
||||
@@ -485,9 +492,10 @@ namespace ts {
|
||||
* @param options - The compiler options which should be used.
|
||||
* @param host - The host interacts with the underlying file system.
|
||||
* @param oldProgram - Reuses an old program structure.
|
||||
* @param configFileParsingDiagnostics - error during config file parsing
|
||||
* @returns A 'Program' object.
|
||||
*/
|
||||
export function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
|
||||
export function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): Program {
|
||||
let program: Program;
|
||||
let files: SourceFile[] = [];
|
||||
let commonSourceDirectory: string;
|
||||
@@ -538,7 +546,7 @@ namespace ts {
|
||||
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModuleFull[];
|
||||
const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
|
||||
if (host.resolveModuleNames) {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile, reusedNames) => host.resolveModuleNames(checkAllDefined(moduleNames), containingFile, reusedNames).map(resolved => {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile, reusedNames) => host.resolveModuleNames(Debug.assertEachDefined(moduleNames), containingFile, reusedNames).map(resolved => {
|
||||
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
|
||||
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
|
||||
return resolved as ResolvedModuleFull;
|
||||
@@ -551,16 +559,16 @@ namespace ts {
|
||||
else {
|
||||
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
|
||||
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule;
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(checkAllDefined(moduleNames), containingFile, loader);
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(Debug.assertEachDefined(moduleNames), containingFile, loader);
|
||||
}
|
||||
|
||||
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
|
||||
if (host.resolveTypeReferenceDirectives) {
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(checkAllDefined(typeDirectiveNames), containingFile);
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(Debug.assertEachDefined(typeDirectiveNames), containingFile);
|
||||
}
|
||||
else {
|
||||
const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, options, host).resolvedTypeReferenceDirective;
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader);
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(Debug.assertEachDefined(typeReferenceDirectiveNames), containingFile, loader);
|
||||
}
|
||||
|
||||
// Map from a stringified PackageId to the source file with that id.
|
||||
@@ -665,7 +673,8 @@ namespace ts {
|
||||
getSourceFileFromReference,
|
||||
sourceFileToPackageName,
|
||||
redirectTargetsSet,
|
||||
isEmittedFile
|
||||
isEmittedFile,
|
||||
getConfigFileParsingDiagnostics
|
||||
};
|
||||
|
||||
verifyCompilerOptions();
|
||||
@@ -1568,6 +1577,10 @@ namespace ts {
|
||||
return sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice());
|
||||
}
|
||||
|
||||
function getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic> {
|
||||
return configFileParsingDiagnostics || emptyArray;
|
||||
}
|
||||
|
||||
function processRootFile(fileName: string, isDefaultLib: boolean) {
|
||||
processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
|
||||
}
|
||||
@@ -2415,11 +2428,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function checkAllDefined(names: string[]): string[] {
|
||||
Debug.assert(names.every(name => name !== undefined), "A name is undefined.", () => JSON.stringify(names));
|
||||
return names;
|
||||
}
|
||||
|
||||
function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] {
|
||||
const res = imports.map(i => i.text);
|
||||
for (const aug of moduleAugmentations) {
|
||||
|
||||
+23
-14
@@ -334,9 +334,10 @@ namespace ts {
|
||||
/*@internal*/
|
||||
export interface RecursiveDirectoryWatcherHost {
|
||||
watchDirectory: HostWatchDirectory;
|
||||
getAccessileSortedChildDirectories(path: string): ReadonlyArray<string>;
|
||||
getAccessibleSortedChildDirectories(path: string): ReadonlyArray<string>;
|
||||
directoryExists(dir: string): boolean;
|
||||
filePathComparer: Comparer<string>;
|
||||
realpath(s: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,9 +393,14 @@ namespace ts {
|
||||
function watchChildDirectories(parentDir: string, existingChildWatches: ChildWatches, callback: DirectoryWatcherCallback): ChildWatches {
|
||||
let newChildWatches: DirectoryWatcher[] | undefined;
|
||||
enumerateInsertsAndDeletes<string, DirectoryWatcher>(
|
||||
host.directoryExists(parentDir) ? host.getAccessileSortedChildDirectories(parentDir) : emptyArray,
|
||||
host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => {
|
||||
const childFullName = getNormalizedAbsolutePath(child, parentDir);
|
||||
// Filter our the symbolic link directories since those arent included in recursive watch
|
||||
// which is same behaviour when recursive: true is passed to fs.watch
|
||||
return host.filePathComparer(childFullName, host.realpath(childFullName)) === Comparison.EqualTo ? childFullName : undefined;
|
||||
}) : emptyArray,
|
||||
existingChildWatches,
|
||||
(child, childWatcher) => host.filePathComparer(getNormalizedAbsolutePath(child, parentDir), childWatcher.dirName),
|
||||
(child, childWatcher) => host.filePathComparer(child, childWatcher.dirName),
|
||||
createAndAddChildDirectoryWatcher,
|
||||
closeFileWatcher,
|
||||
addChildDirectoryWatcher
|
||||
@@ -406,7 +412,7 @@ namespace ts {
|
||||
* Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list
|
||||
*/
|
||||
function createAndAddChildDirectoryWatcher(childName: string) {
|
||||
const result = createDirectoryWatcher(getNormalizedAbsolutePath(childName, parentDir), callback);
|
||||
const result = createDirectoryWatcher(childName, callback);
|
||||
addChildDirectoryWatcher(result);
|
||||
}
|
||||
|
||||
@@ -601,14 +607,7 @@ namespace ts {
|
||||
exit(exitCode?: number): void {
|
||||
process.exit(exitCode);
|
||||
},
|
||||
realpath(path: string): string {
|
||||
try {
|
||||
return _fs.realpathSync(path);
|
||||
}
|
||||
catch {
|
||||
return path;
|
||||
}
|
||||
},
|
||||
realpath,
|
||||
debugMode: some(<string[]>process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
|
||||
tryEnableSourceMapsForHost() {
|
||||
try {
|
||||
@@ -699,8 +698,9 @@ namespace ts {
|
||||
const watchDirectoryRecursively = createRecursiveDirectoryWatcher({
|
||||
filePathComparer: useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
|
||||
directoryExists,
|
||||
getAccessileSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
|
||||
watchDirectory
|
||||
getAccessibleSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
|
||||
watchDirectory,
|
||||
realpath
|
||||
});
|
||||
|
||||
return (directoryName, callback, recursive) => {
|
||||
@@ -1043,6 +1043,15 @@ namespace ts {
|
||||
return filter<string>(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory));
|
||||
}
|
||||
|
||||
function realpath(path: string): string {
|
||||
try {
|
||||
return _fs.realpathSync(path);
|
||||
}
|
||||
catch {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
function getModifiedTime(path: string) {
|
||||
try {
|
||||
return _fs.statSync(path).mtime;
|
||||
|
||||
@@ -135,9 +135,11 @@ namespace ts {
|
||||
if (node.kind === SyntaxKind.Bundle) {
|
||||
isBundledEmit = true;
|
||||
const refs = createMap<SourceFile>();
|
||||
let hasNoDefaultLib = false;
|
||||
const bundle = createBundle(map(node.sourceFiles,
|
||||
sourceFile => {
|
||||
if (sourceFile.isDeclarationFile || isSourceFileJavaScript(sourceFile)) return; // Omit declaration files from bundle results, too
|
||||
hasNoDefaultLib = hasNoDefaultLib || sourceFile.hasNoDefaultLib;
|
||||
currentSourceFile = sourceFile;
|
||||
enclosingDeclaration = sourceFile;
|
||||
possibleImports = undefined;
|
||||
@@ -154,16 +156,17 @@ namespace ts {
|
||||
[createModifier(SyntaxKind.DeclareKeyword)],
|
||||
createLiteral(getResolvedExternalModuleName(context.getEmitHost(), sourceFile)),
|
||||
createModuleBlock(setTextRange(createNodeArray(filterCandidateImports(statements)), sourceFile.statements))
|
||||
)], /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ []);
|
||||
)], /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ [], /*hasNoDefaultLib*/ false);
|
||||
return newFile;
|
||||
}
|
||||
needsDeclare = true;
|
||||
const updated = visitNodes(sourceFile.statements, visitDeclarationStatements);
|
||||
return updateSourceFileNode(sourceFile, updated, /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ []);
|
||||
return updateSourceFileNode(sourceFile, updated, /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ [], /*hasNoDefaultLib*/ false);
|
||||
}
|
||||
));
|
||||
bundle.syntheticFileReferences = [];
|
||||
bundle.syntheticTypeReferences = getFileReferencesForUsedTypeReferences();
|
||||
bundle.hasNoDefaultLib = hasNoDefaultLib;
|
||||
const outputFilePath = getDirectoryPath(normalizeSlashes(getOutputPathsFor(node, host, /*forceDtsPaths*/ true).declarationFilePath));
|
||||
const referenceVisitor = mapReferencesIntoArray(bundle.syntheticFileReferences as FileReference[], outputFilePath);
|
||||
refs.forEach(referenceVisitor);
|
||||
@@ -191,7 +194,7 @@ namespace ts {
|
||||
if (isExternalModule(node) && !resultHasExternalModuleIndicator) {
|
||||
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements);
|
||||
}
|
||||
const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences());
|
||||
const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib);
|
||||
return updated;
|
||||
|
||||
function getFileReferencesForUsedTypeReferences() {
|
||||
|
||||
@@ -1510,8 +1510,7 @@ namespace ts {
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind(node);
|
||||
break;
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
|
||||
const captureNewTargetStatement = createVariableStatement(
|
||||
@@ -1922,7 +1921,7 @@ namespace ts {
|
||||
return block;
|
||||
}
|
||||
|
||||
function visitFunctionBodyDownLevel(node: FunctionDeclaration | FunctionExpression) {
|
||||
function visitFunctionBodyDownLevel(node: FunctionDeclaration | FunctionExpression | AccessorDeclaration) {
|
||||
const updated = visitFunctionBody(node.body, functionBodyVisitor, context);
|
||||
return updateBlock(
|
||||
updated,
|
||||
@@ -3196,18 +3195,15 @@ namespace ts {
|
||||
convertedLoopState = undefined;
|
||||
const ancestorFacts = enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes);
|
||||
let updated: AccessorDeclaration;
|
||||
if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis) {
|
||||
const parameters = visitParameterList(node.parameters, visitor, context);
|
||||
const body = transformFunctionBody(node);
|
||||
if (node.kind === SyntaxKind.GetAccessor) {
|
||||
updated = updateGetAccessor(node, node.decorators, node.modifiers, node.name, parameters, node.type, body);
|
||||
}
|
||||
else {
|
||||
updated = updateSetAccessor(node, node.decorators, node.modifiers, node.name, parameters, body);
|
||||
}
|
||||
const parameters = visitParameterList(node.parameters, visitor, context);
|
||||
const body = node.transformFlags & (TransformFlags.ContainsCapturedLexicalThis | TransformFlags.ContainsES2015)
|
||||
? transformFunctionBody(node)
|
||||
: visitFunctionBodyDownLevel(node);
|
||||
if (node.kind === SyntaxKind.GetAccessor) {
|
||||
updated = updateGetAccessor(node, node.decorators, node.modifiers, node.name, parameters, node.type, body);
|
||||
}
|
||||
else {
|
||||
updated = visitEachChild(node, visitor, context);
|
||||
updated = updateSetAccessor(node, node.decorators, node.modifiers, node.name, parameters, body);
|
||||
}
|
||||
exitSubtree(ancestorFacts, HierarchyFacts.PropagateNewTargetMask, HierarchyFacts.None);
|
||||
convertedLoopState = savedConvertedLoopState;
|
||||
|
||||
@@ -430,8 +430,7 @@ namespace ts {
|
||||
return visitFunctionExpression(<FunctionExpression>node);
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind(node);
|
||||
return visitEachChild(node, visitor, context);
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,7 @@ namespace ts {
|
||||
return visitJsxFragment(node, /*isChild*/ true);
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind(node);
|
||||
return undefined;
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +181,7 @@ namespace ts {
|
||||
return visitJsxExpression(node);
|
||||
}
|
||||
else {
|
||||
Debug.failBadSyntaxKind(node);
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -913,6 +913,12 @@ namespace ts {
|
||||
delete deferredExports[id];
|
||||
return append(statements, node);
|
||||
}
|
||||
else {
|
||||
const original = getOriginalNode(node);
|
||||
if (isModuleOrEnumDeclaration(original)) {
|
||||
return append(appendExportsOfDeclaration(statements, original), node);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -324,8 +324,7 @@ namespace ts {
|
||||
return node;
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind(node);
|
||||
return undefined;
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,8 +530,7 @@ namespace ts {
|
||||
return visitImportEqualsDeclaration(<ImportEqualsDeclaration>node);
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind(node);
|
||||
return visitEachChild(node, visitor, context);
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,10 +1868,8 @@ namespace ts {
|
||||
return createIdentifier("Boolean");
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind((<LiteralTypeNode>node).literal);
|
||||
break;
|
||||
return Debug.failBadSyntaxKind((<LiteralTypeNode>node).literal);
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.NumberKeyword:
|
||||
return createIdentifier("Number");
|
||||
@@ -1900,8 +1896,7 @@ namespace ts {
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.failBadSyntaxKind(node);
|
||||
break;
|
||||
return Debug.failBadSyntaxKind(node);
|
||||
}
|
||||
|
||||
return createIdentifier("Object");
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace ts {
|
||||
// import * as x from "mod"
|
||||
// import { x, y } from "mod"
|
||||
externalImports.push(<ImportDeclaration>node);
|
||||
hasImportStarOrImportDefault = getImportNeedsImportStarHelper(<ImportDeclaration>node) || getImportNeedsImportDefaultHelper(<ImportDeclaration>node);
|
||||
hasImportStarOrImportDefault = hasImportStarOrImportDefault || getImportNeedsImportStarHelper(<ImportDeclaration>node) || getImportNeedsImportDefaultHelper(<ImportDeclaration>node);
|
||||
break;
|
||||
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
|
||||
+4
-7
@@ -117,7 +117,7 @@ namespace ts {
|
||||
createWatchOfConfigFile(configParseResult, commandLineOptions);
|
||||
}
|
||||
else {
|
||||
performCompilation(configParseResult.fileNames, configParseResult.options);
|
||||
performCompilation(configParseResult.fileNames, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -139,11 +139,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions) {
|
||||
function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
|
||||
const compilerHost = createCompilerHost(compilerOptions);
|
||||
enableStatistics(compilerOptions);
|
||||
|
||||
const program = createProgram(rootFileNames, compilerOptions, compilerHost);
|
||||
const program = createProgram(rootFileNames, compilerOptions, compilerHost, /*oldProgram*/ undefined, configFileParsingDiagnostics);
|
||||
const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine));
|
||||
reportStatistics(program);
|
||||
return sys.exit(exitStatus);
|
||||
@@ -169,10 +169,7 @@ namespace ts {
|
||||
function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) {
|
||||
const watchCompilerHost = createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(configParseResult.options));
|
||||
updateWatchCompilationHost(watchCompilerHost);
|
||||
watchCompilerHost.rootFiles = configParseResult.fileNames;
|
||||
watchCompilerHost.options = configParseResult.options;
|
||||
watchCompilerHost.configFileSpecs = configParseResult.configFileSpecs;
|
||||
watchCompilerHost.configFileWildCardDirectories = configParseResult.wildcardDirectories;
|
||||
watchCompilerHost.configFileParsingResult = configParseResult;
|
||||
createWatchProgram(watchCompilerHost);
|
||||
}
|
||||
|
||||
|
||||
@@ -2590,6 +2590,7 @@ namespace ts {
|
||||
sourceFiles: ReadonlyArray<SourceFile>;
|
||||
/* @internal */ syntheticFileReferences?: ReadonlyArray<FileReference>;
|
||||
/* @internal */ syntheticTypeReferences?: ReadonlyArray<FileReference>;
|
||||
/* @internal */ hasNoDefaultLib?: boolean;
|
||||
}
|
||||
|
||||
export interface JsonSourceFile extends SourceFile {
|
||||
@@ -2671,6 +2672,7 @@ namespace ts {
|
||||
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
|
||||
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
|
||||
|
||||
/**
|
||||
* Gets a type checker that can be used to semantically analyze source files in the program.
|
||||
@@ -2778,7 +2780,7 @@ namespace ts {
|
||||
getCompilerOptions(): CompilerOptions;
|
||||
|
||||
getSourceFiles(): ReadonlyArray<SourceFile>;
|
||||
getSourceFile(fileName: string): SourceFile;
|
||||
getSourceFile(fileName: string): SourceFile | undefined;
|
||||
getResolvedTypeReferenceDirectives(): ReadonlyMap<ResolvedTypeReferenceDirective>;
|
||||
}
|
||||
|
||||
@@ -3878,7 +3880,11 @@ namespace ts {
|
||||
resolvedTrueType?: Type;
|
||||
resolvedFalseType?: Type;
|
||||
/* @internal */
|
||||
resolvedInferredTrueType?: Type;
|
||||
/* @internal */
|
||||
mapper?: TypeMapper;
|
||||
/* @internal */
|
||||
combinedMapper?: TypeMapper;
|
||||
}
|
||||
|
||||
// Type parameter substitution (TypeFlags.Substitution)
|
||||
@@ -5087,14 +5093,7 @@ namespace ts {
|
||||
// Otherwise, returns all the diagnostics (global and file associated) in this collection.
|
||||
getDiagnostics(fileName?: string): Diagnostic[];
|
||||
|
||||
// Gets a count of how many times this collection has been modified. This value changes
|
||||
// each time 'add' is called (regardless of whether or not an equivalent diagnostic was
|
||||
// already in the collection). As such, it can be used as a simple way to tell if any
|
||||
// operation caused diagnostics to be returned by storing and comparing the return value
|
||||
// of this method before/after the operation is performed.
|
||||
getModificationCount(): number;
|
||||
|
||||
/* @internal */ reattachFileDiagnostics(newFile: SourceFile): void;
|
||||
reattachFileDiagnostics(newFile: SourceFile): void;
|
||||
}
|
||||
|
||||
// SyntaxKind.SyntaxList
|
||||
|
||||
+129
-115
@@ -2059,12 +2059,12 @@ namespace ts {
|
||||
// Name in member declaration or property name in property access
|
||||
return (<NamedDeclaration | PropertyAccessExpression>parent).name === node;
|
||||
case SyntaxKind.QualifiedName:
|
||||
// Name on right hand side of dot in a type query
|
||||
// Name on right hand side of dot in a type query or type reference
|
||||
if ((<QualifiedName>parent).right === node) {
|
||||
while (parent.kind === SyntaxKind.QualifiedName) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
return parent.kind === SyntaxKind.TypeQuery;
|
||||
return parent.kind === SyntaxKind.TypeQuery || parent.kind === SyntaxKind.TypeReference;
|
||||
}
|
||||
return false;
|
||||
case SyntaxKind.BindingElement:
|
||||
@@ -2111,6 +2111,13 @@ namespace ts {
|
||||
return heritageClause ? heritageClause.types : undefined;
|
||||
}
|
||||
|
||||
/** Returns the node in an `extends` or `implements` clause of a class or interface. */
|
||||
export function getAllSuperTypeNodes(node: Node): ReadonlyArray<TypeNode> {
|
||||
return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray
|
||||
: isClassLike(node) ? concatenate(singleElementArray(getClassExtendsHeritageClauseElement(node)), getClassImplementsHeritageClauseElements(node)) || emptyArray
|
||||
: emptyArray;
|
||||
}
|
||||
|
||||
export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration) {
|
||||
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
|
||||
return heritageClause ? heritageClause.types : undefined;
|
||||
@@ -2414,6 +2421,63 @@ namespace ts {
|
||||
|
||||
export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: SyntaxKind, hasArguments?: boolean) {
|
||||
switch (nodeKind) {
|
||||
case SyntaxKind.CommaListExpression:
|
||||
return 0;
|
||||
|
||||
case SyntaxKind.SpreadElement:
|
||||
return 1;
|
||||
|
||||
case SyntaxKind.YieldExpression:
|
||||
return 2;
|
||||
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return 4;
|
||||
|
||||
case SyntaxKind.BinaryExpression:
|
||||
switch (operatorKind) {
|
||||
case SyntaxKind.CommaToken:
|
||||
return 0;
|
||||
|
||||
case SyntaxKind.EqualsToken:
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
case SyntaxKind.MinusEqualsToken:
|
||||
case SyntaxKind.AsteriskAsteriskEqualsToken:
|
||||
case SyntaxKind.AsteriskEqualsToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
case SyntaxKind.PercentEqualsToken:
|
||||
case SyntaxKind.LessThanLessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.AmpersandEqualsToken:
|
||||
case SyntaxKind.CaretEqualsToken:
|
||||
case SyntaxKind.BarEqualsToken:
|
||||
return 3;
|
||||
|
||||
default:
|
||||
return getBinaryOperatorPrecedence(operatorKind);
|
||||
}
|
||||
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.TypeOfExpression:
|
||||
case SyntaxKind.VoidExpression:
|
||||
case SyntaxKind.DeleteExpression:
|
||||
case SyntaxKind.AwaitExpression:
|
||||
return 16;
|
||||
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return 17;
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
return 18;
|
||||
|
||||
case SyntaxKind.NewExpression:
|
||||
return hasArguments ? 19 : 18;
|
||||
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return 19;
|
||||
|
||||
case SyntaxKind.ThisKeyword:
|
||||
case SyntaxKind.SuperKeyword:
|
||||
case SyntaxKind.Identifier:
|
||||
@@ -2435,137 +2499,72 @@ namespace ts {
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
case SyntaxKind.OmittedExpression:
|
||||
return 19;
|
||||
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return 18;
|
||||
|
||||
case SyntaxKind.NewExpression:
|
||||
return hasArguments ? 18 : 17;
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
return 17;
|
||||
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return 16;
|
||||
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.TypeOfExpression:
|
||||
case SyntaxKind.VoidExpression:
|
||||
case SyntaxKind.DeleteExpression:
|
||||
case SyntaxKind.AwaitExpression:
|
||||
return 15;
|
||||
|
||||
case SyntaxKind.BinaryExpression:
|
||||
switch (operatorKind) {
|
||||
case SyntaxKind.ExclamationToken:
|
||||
case SyntaxKind.TildeToken:
|
||||
return 15;
|
||||
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
return 14;
|
||||
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
return 13;
|
||||
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
return 12;
|
||||
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.InKeyword:
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
return 11;
|
||||
|
||||
case SyntaxKind.EqualsEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
return 10;
|
||||
|
||||
case SyntaxKind.AmpersandToken:
|
||||
return 9;
|
||||
|
||||
case SyntaxKind.CaretToken:
|
||||
return 8;
|
||||
|
||||
case SyntaxKind.BarToken:
|
||||
return 7;
|
||||
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
return 6;
|
||||
|
||||
case SyntaxKind.BarBarToken:
|
||||
return 5;
|
||||
|
||||
case SyntaxKind.EqualsToken:
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
case SyntaxKind.MinusEqualsToken:
|
||||
case SyntaxKind.AsteriskAsteriskEqualsToken:
|
||||
case SyntaxKind.AsteriskEqualsToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
case SyntaxKind.PercentEqualsToken:
|
||||
case SyntaxKind.LessThanLessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.AmpersandEqualsToken:
|
||||
case SyntaxKind.CaretEqualsToken:
|
||||
case SyntaxKind.BarEqualsToken:
|
||||
return 3;
|
||||
|
||||
case SyntaxKind.CommaToken:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return 4;
|
||||
|
||||
case SyntaxKind.YieldExpression:
|
||||
return 2;
|
||||
|
||||
case SyntaxKind.SpreadElement:
|
||||
return 1;
|
||||
|
||||
case SyntaxKind.CommaListExpression:
|
||||
return 0;
|
||||
return 20;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getBinaryOperatorPrecedence(kind: SyntaxKind): number {
|
||||
switch (kind) {
|
||||
case SyntaxKind.BarBarToken:
|
||||
return 5;
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
return 6;
|
||||
case SyntaxKind.BarToken:
|
||||
return 7;
|
||||
case SyntaxKind.CaretToken:
|
||||
return 8;
|
||||
case SyntaxKind.AmpersandToken:
|
||||
return 9;
|
||||
case SyntaxKind.EqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
return 10;
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
case SyntaxKind.InKeyword:
|
||||
case SyntaxKind.AsKeyword:
|
||||
return 11;
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
return 12;
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
return 13;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
return 14;
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
return 15;
|
||||
}
|
||||
|
||||
// -1 is lower than all other precedences. Returning it will cause binary expression
|
||||
// parsing to stop.
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function createDiagnosticCollection(): DiagnosticCollection {
|
||||
let nonFileDiagnostics = [] as SortedArray<Diagnostic>;
|
||||
const filesWithDiagnostics = [] as SortedArray<string>;
|
||||
const fileDiagnostics = createMap<SortedArray<Diagnostic>>();
|
||||
let hasReadNonFileDiagnostics = false;
|
||||
let modificationCount = 0;
|
||||
|
||||
return {
|
||||
add,
|
||||
getGlobalDiagnostics,
|
||||
getDiagnostics,
|
||||
getModificationCount,
|
||||
reattachFileDiagnostics
|
||||
};
|
||||
|
||||
function getModificationCount() {
|
||||
return modificationCount;
|
||||
}
|
||||
|
||||
function reattachFileDiagnostics(newFile: SourceFile): void {
|
||||
forEach(fileDiagnostics.get(newFile.fileName), diagnostic => diagnostic.file = newFile);
|
||||
}
|
||||
@@ -2591,7 +2590,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
insertSorted(diagnostics, diagnostic, compareDiagnostics);
|
||||
modificationCount++;
|
||||
}
|
||||
|
||||
function getGlobalDiagnostics(): Diagnostic[] {
|
||||
@@ -4070,6 +4068,12 @@ namespace ts {
|
||||
return { start, length };
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createTextRange(pos: number, end: number = pos): TextRange {
|
||||
Debug.assert(end >= pos);
|
||||
return { pos, end };
|
||||
}
|
||||
|
||||
export function createTextSpanFromBounds(start: number, end: number) {
|
||||
return createTextSpan(start, end - start);
|
||||
}
|
||||
@@ -5432,6 +5436,16 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isParameterPropertyModifier(kind: SyntaxKind): boolean {
|
||||
return !!(modifierToFlag(kind) & ModifierFlags.ParameterPropertyModifier);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isClassMemberModifier(idToken: SyntaxKind): boolean {
|
||||
return isParameterPropertyModifier(idToken) || idToken === SyntaxKind.StaticKeyword;
|
||||
}
|
||||
|
||||
export function isModifier(node: Node): node is Modifier {
|
||||
return isModifierKind(node.kind);
|
||||
}
|
||||
|
||||
@@ -1546,11 +1546,11 @@ namespace ts {
|
||||
export namespace Debug {
|
||||
let isDebugInfoEnabled = false;
|
||||
|
||||
export const failBadSyntaxKind = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, message?: string): never => fail(
|
||||
export function failBadSyntaxKind(node: Node, message?: string): never {
|
||||
return fail(
|
||||
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
|
||||
failBadSyntaxKind)
|
||||
: noop as () => never; // TODO: GH#22091
|
||||
failBadSyntaxKind);
|
||||
}
|
||||
|
||||
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
|
||||
|
||||
+52
-40
@@ -70,10 +70,8 @@ namespace ts {
|
||||
/** Parses config file using System interface */
|
||||
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) {
|
||||
const host: ParseConfigFileHost = <any>system;
|
||||
host.onConfigFileDiagnostic = reportDiagnostic;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic);
|
||||
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host);
|
||||
host.onConfigFileDiagnostic = undefined;
|
||||
host.onUnRecoverableConfigFileDiagnostic = undefined;
|
||||
return result;
|
||||
}
|
||||
@@ -98,13 +96,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
const result = parseJsonText(configFileName, configFileText);
|
||||
result.parseDiagnostics.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic));
|
||||
|
||||
const cwd = host.getCurrentDirectory();
|
||||
const configParseResult = parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
configParseResult.errors.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic));
|
||||
|
||||
return configParseResult;
|
||||
return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,6 +111,7 @@ namespace ts {
|
||||
getOptionsDiagnostics(): ReadonlyArray<Diagnostic>;
|
||||
getGlobalDiagnostics(): ReadonlyArray<Diagnostic>;
|
||||
getSemanticDiagnostics(): ReadonlyArray<Diagnostic>;
|
||||
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
|
||||
emit(): EmitResult;
|
||||
}
|
||||
|
||||
@@ -126,16 +120,18 @@ namespace ts {
|
||||
*/
|
||||
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void) {
|
||||
// First get and report any syntactic errors.
|
||||
const diagnostics = program.getSyntacticDiagnostics().slice();
|
||||
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
|
||||
const configFileParsingDiagnosticsLength = diagnostics.length;
|
||||
addRange(diagnostics, program.getSyntacticDiagnostics());
|
||||
let reportSemanticDiagnostics = false;
|
||||
|
||||
// If we didn't have any syntactic errors, then also try getting the global and
|
||||
// semantic errors.
|
||||
if (diagnostics.length === 0) {
|
||||
if (diagnostics.length === configFileParsingDiagnosticsLength) {
|
||||
addRange(diagnostics, program.getOptionsDiagnostics());
|
||||
addRange(diagnostics, program.getGlobalDiagnostics());
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
if (diagnostics.length === configFileParsingDiagnosticsLength) {
|
||||
reportSemanticDiagnostics = true;
|
||||
}
|
||||
}
|
||||
@@ -238,7 +234,6 @@ namespace ts {
|
||||
export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T> {
|
||||
reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
|
||||
const host = createWatchCompilerHost(system, createProgram, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>;
|
||||
host.onConfigFileDiagnostic = reportDiagnostic;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
|
||||
host.configFileName = configFileName;
|
||||
host.optionsToExtend = optionsToExtend;
|
||||
@@ -259,7 +254,8 @@ namespace ts {
|
||||
namespace ts {
|
||||
export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
|
||||
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
|
||||
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: T) => T;
|
||||
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
|
||||
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
|
||||
export interface WatchCompilerHost<T extends BuilderProgram> {
|
||||
/**
|
||||
* Used to create the program when need for program creation or recreation detected
|
||||
@@ -345,11 +341,6 @@ namespace ts {
|
||||
* Reports config file diagnostics
|
||||
*/
|
||||
export interface ConfigFileDiagnosticsReporter {
|
||||
/**
|
||||
* Reports the diagnostics in reading/writing or parsing of the config file
|
||||
*/
|
||||
onConfigFileDiagnostic: DiagnosticReporter;
|
||||
|
||||
/**
|
||||
* Reports unrecoverable error when parsing config file
|
||||
*/
|
||||
@@ -378,11 +369,8 @@ namespace ts {
|
||||
*/
|
||||
/*@internal*/
|
||||
export interface WatchCompilerHostOfConfigFile<T extends BuilderProgram> extends WatchCompilerHost<T> {
|
||||
rootFiles?: string[];
|
||||
options?: CompilerOptions;
|
||||
optionsToExtend?: CompilerOptions;
|
||||
configFileSpecs?: ConfigFileSpecs;
|
||||
configFileWildCardDirectories?: MapLike<WatchDirectoryFlags>;
|
||||
configFileParsingResult?: ParsedCommandLine;
|
||||
}
|
||||
|
||||
export interface Watch<T> {
|
||||
@@ -460,7 +448,10 @@ namespace ts {
|
||||
const getCurrentDirectory = () => currentDirectory;
|
||||
const readFile: (path: string, encoding?: string) => string | undefined = (path, encoding) => host.readFile(path, encoding);
|
||||
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, createProgram } = host;
|
||||
let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host;
|
||||
let { rootFiles: rootFileNames, options: compilerOptions } = host;
|
||||
let configFileSpecs: ConfigFileSpecs;
|
||||
let configFileParsingDiagnostics: ReadonlyArray<Diagnostic> | undefined;
|
||||
let hasChangedConfigFileParsingErrors = false;
|
||||
|
||||
const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames);
|
||||
if (cachedDirectoryStructureHost && host.onCachedDirectoryStructureHostCreate) {
|
||||
@@ -473,13 +464,22 @@ namespace ts {
|
||||
fileExists: path => host.fileExists(path),
|
||||
readFile,
|
||||
getCurrentDirectory,
|
||||
onConfigFileDiagnostic: host.onConfigFileDiagnostic,
|
||||
onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic
|
||||
};
|
||||
|
||||
// From tsc we want to get already parsed result and hence check for rootFileNames
|
||||
if (configFileName && !rootFileNames) {
|
||||
parseConfigFile();
|
||||
let newLine = updateNewLine();
|
||||
reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode);
|
||||
if (configFileName) {
|
||||
newLine = getNewLineCharacter(optionsToExtendForConfigFile, () => host.getNewLine());
|
||||
if (host.configFileParsingResult) {
|
||||
setConfigFileParsingResult(host.configFileParsingResult);
|
||||
}
|
||||
else {
|
||||
Debug.assert(!rootFileNames);
|
||||
parseConfigFile();
|
||||
}
|
||||
newLine = updateNewLine();
|
||||
}
|
||||
|
||||
const trace = host.trace && ((s: string) => { host.trace(s + newLine); });
|
||||
@@ -489,7 +489,6 @@ namespace ts {
|
||||
const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(watchLogLevel, writeLog);
|
||||
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
let newLine = updateNewLine();
|
||||
|
||||
writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`);
|
||||
if (configFileName) {
|
||||
@@ -546,7 +545,6 @@ namespace ts {
|
||||
((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile));
|
||||
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
|
||||
|
||||
reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode);
|
||||
synchronizeProgram();
|
||||
|
||||
// Update the wild card directory watch
|
||||
@@ -578,6 +576,10 @@ namespace ts {
|
||||
// All resolutions are invalid if user provided resolutions
|
||||
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution);
|
||||
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) {
|
||||
if (hasChangedConfigFileParsingErrors) {
|
||||
builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics);
|
||||
hasChangedConfigFileParsingErrors = false;
|
||||
}
|
||||
return builderProgram;
|
||||
}
|
||||
|
||||
@@ -590,10 +592,11 @@ namespace ts {
|
||||
|
||||
const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program;
|
||||
hasChangedCompilerOptions = false;
|
||||
hasChangedConfigFileParsingErrors = false;
|
||||
resolutionCache.startCachingPerDirectoryResolution();
|
||||
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
|
||||
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
|
||||
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram);
|
||||
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram, configFileParsingDiagnostics);
|
||||
resolutionCache.finishCachingPerDirectoryResolution();
|
||||
|
||||
// Update watches
|
||||
@@ -630,7 +633,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function updateNewLine() {
|
||||
return getNewLineCharacter(compilerOptions, () => host.getNewLine());
|
||||
return getNewLineCharacter(compilerOptions || optionsToExtendForConfigFile, () => host.getNewLine());
|
||||
}
|
||||
|
||||
function toPath(fileName: string) {
|
||||
@@ -760,7 +763,7 @@ namespace ts {
|
||||
|
||||
function reportWatchDiagnostic(message: DiagnosticMessage) {
|
||||
if (host.onWatchStatusChange) {
|
||||
host.onWatchStatusChange(createCompilerDiagnostic(message), newLine, compilerOptions);
|
||||
host.onWatchStatusChange(createCompilerDiagnostic(message), newLine, compilerOptions || optionsToExtendForConfigFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,8 +804,13 @@ namespace ts {
|
||||
|
||||
function reloadFileNamesFromConfigFile() {
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost);
|
||||
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
||||
host.onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||
if (result.fileNames.length) {
|
||||
configFileParsingDiagnostics = filter(configFileParsingDiagnostics, error => !isErrorNoInputFiles(error));
|
||||
hasChangedConfigFileParsingErrors = true;
|
||||
}
|
||||
else if (!configFileSpecs.filesSpecs && !some(configFileParsingDiagnostics, isErrorNoInputFiles)) {
|
||||
configFileParsingDiagnostics = configFileParsingDiagnostics!.concat(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||
hasChangedConfigFileParsingErrors = true;
|
||||
}
|
||||
rootFileNames = result.fileNames;
|
||||
|
||||
@@ -826,11 +834,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseConfigFile() {
|
||||
const configParseResult = getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost);
|
||||
rootFileNames = configParseResult.fileNames;
|
||||
compilerOptions = configParseResult.options;
|
||||
configFileSpecs = configParseResult.configFileSpecs;
|
||||
configFileWildCardDirectories = configParseResult.wildcardDirectories;
|
||||
setConfigFileParsingResult(getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost));
|
||||
}
|
||||
|
||||
function setConfigFileParsingResult(configFileParseResult: ParsedCommandLine) {
|
||||
rootFileNames = configFileParseResult.fileNames;
|
||||
compilerOptions = configFileParseResult.options;
|
||||
configFileSpecs = configFileParseResult.configFileSpecs;
|
||||
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult);
|
||||
hasChangedConfigFileParsingErrors = true;
|
||||
}
|
||||
|
||||
function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) {
|
||||
@@ -876,10 +888,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
function watchConfigFileWildCardDirectories() {
|
||||
if (configFileWildCardDirectories) {
|
||||
if (configFileSpecs) {
|
||||
updateWatchingWildcardDirectories(
|
||||
watchedWildcardDirectories || (watchedWildcardDirectories = createMap()),
|
||||
createMapFromTemplate(configFileWildCardDirectories),
|
||||
createMapFromTemplate(configFileSpecs.wildcardDirectories),
|
||||
watchWildcardDirectory
|
||||
);
|
||||
}
|
||||
|
||||
+23
-17
@@ -832,7 +832,12 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionsAt(markerName: string, expected: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>, options?: FourSlashInterface.CompletionsAtOptions) {
|
||||
public verifyCompletionsAt(markerName: string | ReadonlyArray<string>, expected: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>, options?: FourSlashInterface.CompletionsAtOptions) {
|
||||
if (typeof markerName !== "string") {
|
||||
for (const m of markerName) this.verifyCompletionsAt(m, expected, options);
|
||||
return;
|
||||
}
|
||||
|
||||
this.goToMarker(markerName);
|
||||
|
||||
const actualCompletions = this.getCompletionListAtCaret(options);
|
||||
@@ -840,8 +845,8 @@ namespace FourSlash {
|
||||
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
|
||||
}
|
||||
|
||||
if (options && options.isNewIdentifierLocation !== undefined && actualCompletions.isNewIdentifierLocation !== options.isNewIdentifierLocation) {
|
||||
this.raiseError(`Expected 'isNewIdentifierLocation' to be ${options.isNewIdentifierLocation}, got ${actualCompletions.isNewIdentifierLocation}`);
|
||||
if (actualCompletions.isNewIdentifierLocation !== (options && options.isNewIdentifierLocation || false)) {
|
||||
this.raiseError(`Expected 'isNewIdentifierLocation' to be ${options && options.isNewIdentifierLocation}, got ${actualCompletions.isNewIdentifierLocation}`);
|
||||
}
|
||||
|
||||
const actual = actualCompletions.entries;
|
||||
@@ -2452,12 +2457,14 @@ Actual: ${stringify(fullActual)}`);
|
||||
this.verifyRangeIs(expectedText, includeWhiteSpace);
|
||||
}
|
||||
|
||||
public verifyCodeFixAll(options: FourSlashInterface.VerifyCodeFixAllOptions): void {
|
||||
const { fixId, newFileContent } = options;
|
||||
const fixIds = ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId);
|
||||
ts.Debug.assert(ts.contains(fixIds, fixId), "No available code fix has that group id.", () => `Expected '${fixId}'. Available action ids: ${fixIds}`);
|
||||
public verifyCodeFixAll({ fixId, fixAllDescription, newFileContent, commands: expectedCommands }: FourSlashInterface.VerifyCodeFixAllOptions): void {
|
||||
const fixWithId = ts.find(this.getCodeFixes(this.activeFile.fileName), a => a.fixId === fixId);
|
||||
ts.Debug.assert(fixWithId !== undefined, "No available code fix has that group id.", () =>
|
||||
`Expected '${fixId}'. Available action ids: ${ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId)}`);
|
||||
ts.Debug.assertEqual(fixWithId.fixAllDescription, fixAllDescription);
|
||||
|
||||
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultPreferences);
|
||||
assert.deepEqual(commands, options.commands);
|
||||
assert.deepEqual(commands, expectedCommands);
|
||||
assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files");
|
||||
this.applyChanges(changes);
|
||||
this.verifyCurrentFileContent(newFileContent);
|
||||
@@ -2859,7 +2866,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
|
||||
public verifyRangesWithSameTextAreDocumentHighlights() {
|
||||
this.rangesByText().forEach(ranges => this.verifyRangesAreDocumentHighlights(ranges));
|
||||
this.rangesByText().forEach(ranges => this.verifyRangesAreDocumentHighlights(ranges, /*options*/ undefined));
|
||||
}
|
||||
|
||||
public verifyDocumentHighlightsOf(startRange: Range, ranges: Range[], options: FourSlashInterface.VerifyDocumentHighlightsOptions | undefined) {
|
||||
@@ -2868,9 +2875,9 @@ Actual: ${stringify(fullActual)}`);
|
||||
this.verifyDocumentHighlights(ranges, fileNames);
|
||||
}
|
||||
|
||||
public verifyRangesAreDocumentHighlights(ranges?: Range[]) {
|
||||
public verifyRangesAreDocumentHighlights(ranges: Range[] | undefined, options: FourSlashInterface.VerifyDocumentHighlightsOptions | undefined) {
|
||||
ranges = ranges || this.getRanges();
|
||||
const fileNames = unique(ranges, range => range.fileName);
|
||||
const fileNames = options && options.filesToSearch || unique(ranges, range => range.fileName);
|
||||
for (const range of ranges) {
|
||||
this.goToRangeStart(range);
|
||||
this.verifyDocumentHighlights(ranges, fileNames);
|
||||
@@ -3182,9 +3189,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
eq(item.hasAction, hasAction, "hasAction");
|
||||
eq(item.isRecommended, options && options.isRecommended, "isRecommended");
|
||||
eq(item.insertText, options && options.insertText, "insertText");
|
||||
if (options && options.replacementSpan) { // TODO: GH#21679
|
||||
eq(item.replacementSpan, options && options.replacementSpan && ts.createTextSpanFromRange(options.replacementSpan), "replacementSpan");
|
||||
}
|
||||
eq(item.replacementSpan, options && options.replacementSpan && ts.createTextSpanFromRange(options.replacementSpan), "replacementSpan");
|
||||
}
|
||||
|
||||
private findFile(indexOrName: string | number) {
|
||||
@@ -3988,7 +3993,7 @@ namespace FourSlashInterface {
|
||||
super(state);
|
||||
}
|
||||
|
||||
public completionsAt(markerName: string, completions: ReadonlyArray<ExpectedCompletionEntry>, options?: CompletionsAtOptions) {
|
||||
public completionsAt(markerName: string | ReadonlyArray<string>, completions: ReadonlyArray<ExpectedCompletionEntry>, options?: CompletionsAtOptions) {
|
||||
this.state.verifyCompletionsAt(markerName, completions, options);
|
||||
}
|
||||
|
||||
@@ -4271,8 +4276,8 @@ namespace FourSlashInterface {
|
||||
this.state.verifyRangesAreRenameLocations(options);
|
||||
}
|
||||
|
||||
public rangesAreDocumentHighlights(ranges?: FourSlash.Range[]) {
|
||||
this.state.verifyRangesAreDocumentHighlights(ranges);
|
||||
public rangesAreDocumentHighlights(ranges?: FourSlash.Range[], options?: VerifyDocumentHighlightsOptions) {
|
||||
this.state.verifyRangesAreDocumentHighlights(ranges, options);
|
||||
}
|
||||
|
||||
public rangesWithSameTextAreDocumentHighlights() {
|
||||
@@ -4658,6 +4663,7 @@ namespace FourSlashInterface {
|
||||
|
||||
export interface VerifyCodeFixAllOptions {
|
||||
fixId: string;
|
||||
fixAllDescription: string;
|
||||
newFileContent: string;
|
||||
commands: ReadonlyArray<{}>;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace ts {
|
||||
// github #18071
|
||||
printsCorrectly("regularExpressionLiteral", {}, printer => printer.printFile(createSourceFile("source.ts", "let regex = /abc/;", ScriptTarget.ES2017)));
|
||||
|
||||
// github #22239
|
||||
printsCorrectly("importStatementRemoveComments", { removeComments: true }, printer => printer.printFile(createSourceFile("source.ts", "import {foo} from 'foo';", ScriptTarget.ESNext)));
|
||||
printsCorrectly("classHeritageClauses", {}, printer => printer.printFile(createSourceFile(
|
||||
"source.ts",
|
||||
`class A extends B implements C implements D {}`,
|
||||
|
||||
@@ -72,22 +72,29 @@ namespace ts.tscWatch {
|
||||
checkOutputDoesNotContain(host, expectedNonAffectedFiles);
|
||||
}
|
||||
|
||||
const elapsedRegex = /^Elapsed:: [0-9]+ms/;
|
||||
function checkOutputErrors(
|
||||
host: WatchedSystem,
|
||||
preErrorsWatchDiagnostic: DiagnosticMessage | undefined,
|
||||
logsBeforeWatchDiagnostic: string[] | undefined,
|
||||
preErrorsWatchDiagnostic: DiagnosticMessage,
|
||||
logsBeforeErrors: string[] | undefined,
|
||||
errors: ReadonlyArray<Diagnostic>,
|
||||
disableConsoleClears?: boolean | undefined,
|
||||
...postErrorsWatchDiagnostics: DiagnosticMessage[]
|
||||
) {
|
||||
let screenClears = 0;
|
||||
const outputs = host.getOutput();
|
||||
const expectedOutputCount = (preErrorsWatchDiagnostic ? 1 : 0) + errors.length + postErrorsWatchDiagnostics.length;
|
||||
assert.equal(outputs.length, expectedOutputCount);
|
||||
const expectedOutputCount = 1 + errors.length + postErrorsWatchDiagnostics.length +
|
||||
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) + (logsBeforeErrors ? logsBeforeErrors.length : 0);
|
||||
assert.equal(outputs.length, expectedOutputCount, JSON.stringify(outputs));
|
||||
let index = 0;
|
||||
if (preErrorsWatchDiagnostic) {
|
||||
assertWatchDiagnostic(preErrorsWatchDiagnostic);
|
||||
}
|
||||
forEach(logsBeforeWatchDiagnostic, log => assertLog("logsBeforeWatchDiagnostic", log));
|
||||
assertWatchDiagnostic(preErrorsWatchDiagnostic);
|
||||
forEach(logsBeforeErrors, log => assertLog("logBeforeError", log));
|
||||
// Verify errors
|
||||
forEach(errors, assertDiagnostic);
|
||||
forEach(postErrorsWatchDiagnostics, assertWatchDiagnostic);
|
||||
assert.equal(host.screenClears.length, screenClears, "Expected number of screen clears");
|
||||
host.clearOutput();
|
||||
|
||||
function assertDiagnostic(diagnostic: Diagnostic) {
|
||||
@@ -96,8 +103,18 @@ namespace ts.tscWatch {
|
||||
index++;
|
||||
}
|
||||
|
||||
function assertLog(caption: string, expected: string) {
|
||||
const actual = outputs[index];
|
||||
assert.equal(actual.replace(elapsedRegex, ""), expected.replace(elapsedRegex, ""), getOutputAtFailedMessage(caption, expected));
|
||||
index++;
|
||||
}
|
||||
|
||||
function assertWatchDiagnostic(diagnosticMessage: DiagnosticMessage) {
|
||||
const expected = getWatchDiagnosticWithoutDate(diagnosticMessage);
|
||||
if (!disableConsoleClears && diagnosticMessage.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) {
|
||||
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
|
||||
screenClears++;
|
||||
}
|
||||
assert.isTrue(endsWith(outputs[index], expected), getOutputAtFailedMessage("Watch diagnostic", expected));
|
||||
index++;
|
||||
}
|
||||
@@ -111,20 +128,16 @@ namespace ts.tscWatch {
|
||||
}
|
||||
}
|
||||
|
||||
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) {
|
||||
checkOutputErrors(host, Diagnostics.Starting_compilation_in_watch_mode, errors, Diagnostics.Compilation_complete_Watching_for_file_changes);
|
||||
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
|
||||
checkOutputErrors(host, /*logsBeforeWatchDiagnostic*/ undefined, Diagnostics.Starting_compilation_in_watch_mode, logsBeforeErrors, errors, disableConsoleClears, Diagnostics.Compilation_complete_Watching_for_file_changes);
|
||||
}
|
||||
|
||||
function checkOutputErrorsInitialWithConfigErrors(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) {
|
||||
checkOutputErrors(host, /*preErrorsWatchDiagnostic*/ undefined, errors, Diagnostics.Starting_compilation_in_watch_mode, Diagnostics.Compilation_complete_Watching_for_file_changes);
|
||||
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
|
||||
checkOutputErrors(host, logsBeforeWatchDiagnostic, Diagnostics.File_change_detected_Starting_incremental_compilation, logsBeforeErrors, errors, disableConsoleClears, Diagnostics.Compilation_complete_Watching_for_file_changes);
|
||||
}
|
||||
|
||||
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) {
|
||||
checkOutputErrors(host, Diagnostics.File_change_detected_Starting_incremental_compilation, errors, Diagnostics.Compilation_complete_Watching_for_file_changes);
|
||||
}
|
||||
|
||||
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, expectedExitCode: ExitStatus) {
|
||||
checkOutputErrors(host, Diagnostics.File_change_detected_Starting_incremental_compilation, errors);
|
||||
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
|
||||
checkOutputErrors(host, logsBeforeWatchDiagnostic, Diagnostics.File_change_detected_Starting_incremental_compilation, logsBeforeErrors, errors, disableConsoleClears);
|
||||
assert.equal(host.exitCode, expectedExitCode);
|
||||
}
|
||||
|
||||
@@ -897,7 +910,7 @@ namespace ts.tscWatch {
|
||||
|
||||
const host = createWatchedSystem([file, configFile, libFile]);
|
||||
const watch = createWatchOfConfigFile(configFile.path, host);
|
||||
checkOutputErrorsInitialWithConfigErrors(host, [
|
||||
checkOutputErrorsInitial(host, [
|
||||
getUnknownCompilerOption(watch(), configFile, "foo"),
|
||||
getUnknownCompilerOption(watch(), configFile, "allowJS")
|
||||
]);
|
||||
@@ -2183,30 +2196,32 @@ declare module "fs" {
|
||||
path: "f.ts",
|
||||
content: ""
|
||||
};
|
||||
const files = [file];
|
||||
const files = [file, libFile];
|
||||
const disableConsoleClear = options.diagnostics || options.extendedDiagnostics || options.preserveWatchOutput;
|
||||
const host = createWatchedSystem(files);
|
||||
let clearCount: number | undefined;
|
||||
checkConsoleClears();
|
||||
|
||||
createWatchOfFilesAndCompilerOptions([file.path], host, options);
|
||||
checkConsoleClears();
|
||||
checkOutputErrorsInitial(host, emptyArray, disableConsoleClear, options.extendedDiagnostics && [
|
||||
"Current directory: / CaseSensitiveFileNames: false\n",
|
||||
"Synchronizing program\n",
|
||||
"CreatingProgramWith::\n",
|
||||
" roots: [\"f.ts\"]\n",
|
||||
" options: {\"extendedDiagnostics\":true}\n",
|
||||
"FileWatcher:: Added:: WatchInfo: f.ts 250 \n",
|
||||
"FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 \n"
|
||||
]);
|
||||
|
||||
file.content = "//";
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
checkConsoleClears();
|
||||
|
||||
function checkConsoleClears() {
|
||||
if (clearCount === undefined || options.preserveWatchOutput) {
|
||||
clearCount = 0;
|
||||
}
|
||||
else if (!options.diagnostics && !options.extendedDiagnostics) {
|
||||
clearCount++;
|
||||
}
|
||||
host.checkScreenClears(clearCount);
|
||||
return clearCount;
|
||||
}
|
||||
checkOutputErrorsIncremental(host, emptyArray, disableConsoleClear, options.extendedDiagnostics && [
|
||||
"FileWatcher:: Triggered with /f.ts1:: WatchInfo: f.ts 250 \n",
|
||||
"Elapsed:: 0ms FileWatcher:: Triggered with /f.ts1:: WatchInfo: f.ts 250 \n"
|
||||
], options.extendedDiagnostics && [
|
||||
"Synchronizing program\n",
|
||||
"CreatingProgramWith::\n",
|
||||
" roots: [\"f.ts\"]\n",
|
||||
" options: {\"extendedDiagnostics\":true}\n"
|
||||
]);
|
||||
}
|
||||
|
||||
it("without --diagnostics or --extendedDiagnostics", () => {
|
||||
@@ -2358,6 +2373,50 @@ declare module "fs" {
|
||||
it("uses non recursive dynamic polling when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.DynamicPolling);
|
||||
});
|
||||
|
||||
it("when there are symlinks to folders in recursive folders", () => {
|
||||
const cwd = "/home/user/projects/myproject";
|
||||
const file1: FileOrFolder = {
|
||||
path: `${cwd}/src/file.ts`,
|
||||
content: `import * as a from "a"`
|
||||
};
|
||||
const tsconfig: FileOrFolder = {
|
||||
path: `${cwd}/tsconfig.json`,
|
||||
content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}`
|
||||
};
|
||||
const realA: FileOrFolder = {
|
||||
path: `${cwd}/node_modules/reala/index.d.ts`,
|
||||
content: `export {}`
|
||||
};
|
||||
const realB: FileOrFolder = {
|
||||
path: `${cwd}/node_modules/realb/index.d.ts`,
|
||||
content: `export {}`
|
||||
};
|
||||
const symLinkA: FileOrFolder = {
|
||||
path: `${cwd}/node_modules/a`,
|
||||
symLink: `${cwd}/node_modules/reala`
|
||||
};
|
||||
const symLinkB: FileOrFolder = {
|
||||
path: `${cwd}/node_modules/b`,
|
||||
symLink: `${cwd}/node_modules/realb`
|
||||
};
|
||||
const symLinkBInA: FileOrFolder = {
|
||||
path: `${cwd}/node_modules/reala/node_modules/b`,
|
||||
symLink: `${cwd}/node_modules/b`
|
||||
};
|
||||
const symLinkAInB: FileOrFolder = {
|
||||
path: `${cwd}/node_modules/realb/node_modules/a`,
|
||||
symLink: `${cwd}/node_modules/a`
|
||||
};
|
||||
const files = [file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
const host = createWatchedSystem(files, { environmentVariables, currentDirectory: cwd });
|
||||
createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [cwd, `${cwd}/node_modules`, `${cwd}/node_modules/@types`, `${cwd}/node_modules/reala`, `${cwd}/node_modules/realb`,
|
||||
`${cwd}/node_modules/reala/node_modules`, `${cwd}/node_modules/realb/node_modules`, `${cwd}/src`], /*recursive*/ false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ interface Array<T> {}`
|
||||
private toPath: (f: string) => Path;
|
||||
private timeoutCallbacks = new Callbacks();
|
||||
private immediateCallbacks = new Callbacks();
|
||||
private screenClears = 0;
|
||||
readonly screenClears: number[] = [];
|
||||
|
||||
readonly watchedDirectories = createMultiMap<TestDirectoryWatcher>();
|
||||
readonly watchedDirectoriesRecursive = createMultiMap<TestDirectoryWatcher>();
|
||||
@@ -312,18 +312,20 @@ interface Array<T> {}`
|
||||
const watchDirectory: HostWatchDirectory = (directory, cb) => this.watchFile(directory, () => cb(directory), PollingInterval.Medium);
|
||||
this.customRecursiveWatchDirectory = createRecursiveDirectoryWatcher({
|
||||
directoryExists: path => this.directoryExists(path),
|
||||
getAccessileSortedChildDirectories: path => this.getDirectories(path),
|
||||
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
|
||||
filePathComparer: this.useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
|
||||
watchDirectory
|
||||
watchDirectory,
|
||||
realpath: s => this.realpath(s)
|
||||
});
|
||||
}
|
||||
else if (tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory) {
|
||||
const watchDirectory: HostWatchDirectory = (directory, cb) => this.watchDirectory(directory, fileName => cb(fileName), /*recursive*/ false);
|
||||
this.customRecursiveWatchDirectory = createRecursiveDirectoryWatcher({
|
||||
directoryExists: path => this.directoryExists(path),
|
||||
getAccessileSortedChildDirectories: path => this.getDirectories(path),
|
||||
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
|
||||
filePathComparer: this.useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
|
||||
watchDirectory
|
||||
watchDirectory,
|
||||
realpath: s => this.realpath(s)
|
||||
});
|
||||
}
|
||||
else if (tscWatchDirectory === Tsc_WatchDirectory.DynamicPolling) {
|
||||
@@ -331,9 +333,10 @@ interface Array<T> {}`
|
||||
const watchDirectory: HostWatchDirectory = (directory, cb) => watchFile(directory, () => cb(directory), PollingInterval.Medium);
|
||||
this.customRecursiveWatchDirectory = createRecursiveDirectoryWatcher({
|
||||
directoryExists: path => this.directoryExists(path),
|
||||
getAccessileSortedChildDirectories: path => this.getDirectories(path),
|
||||
getAccessibleSortedChildDirectories: path => this.getDirectories(path),
|
||||
filePathComparer: this.useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
|
||||
watchDirectory
|
||||
watchDirectory,
|
||||
realpath: s => this.realpath(s)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -649,7 +652,7 @@ interface Array<T> {}`
|
||||
|
||||
const realpath = this.realpath(path);
|
||||
if (path !== realpath) {
|
||||
return this.getRealFsEntry(isFsEntry, realpath as Path);
|
||||
return this.getRealFsEntry(isFsEntry, this.toPath(realpath));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -778,7 +781,7 @@ interface Array<T> {}`
|
||||
}
|
||||
|
||||
clearScreen(): void {
|
||||
this.screenClears += 1;
|
||||
this.screenClears.push(this.output.length);
|
||||
}
|
||||
|
||||
checkTimeoutQueueLengthAndRun(expected: number) {
|
||||
@@ -818,10 +821,6 @@ interface Array<T> {}`
|
||||
this.immediateCallbacks.unregister(timeoutId);
|
||||
}
|
||||
|
||||
checkScreenClears(expected: number): void {
|
||||
assert.equal(this.screenClears, expected);
|
||||
}
|
||||
|
||||
createDirectory(directoryName: string): void {
|
||||
const folder = this.toFolder(directoryName);
|
||||
|
||||
@@ -855,6 +854,7 @@ interface Array<T> {}`
|
||||
|
||||
clearOutput() {
|
||||
clear(this.output);
|
||||
this.screenClears.length = 0;
|
||||
}
|
||||
|
||||
realpath(s: string): string {
|
||||
|
||||
Vendored
+27
-21
@@ -3285,6 +3285,7 @@ interface DOMTokenList {
|
||||
contains(token: string): boolean;
|
||||
item(index: number): string | null;
|
||||
remove(...tokens: string[]): void;
|
||||
replace(oldToken: string, newToken: string): void;
|
||||
toString(): string;
|
||||
toggle(token: string, force?: boolean): boolean;
|
||||
[index: number]: string;
|
||||
@@ -3532,10 +3533,10 @@ interface DocumentEventMap extends GlobalEventHandlersEventMap {
|
||||
"submit": Event;
|
||||
"suspend": Event;
|
||||
"timeupdate": Event;
|
||||
"touchcancel": Event;
|
||||
"touchend": Event;
|
||||
"touchmove": Event;
|
||||
"touchstart": Event;
|
||||
"touchcancel": TouchEvent;
|
||||
"touchend": TouchEvent;
|
||||
"touchmove": TouchEvent;
|
||||
"touchstart": TouchEvent;
|
||||
"volumechange": Event;
|
||||
"waiting": Event;
|
||||
"webkitfullscreenchange": Event;
|
||||
@@ -3949,10 +3950,10 @@ interface Document extends Node, GlobalEventHandlers, ParentNode, DocumentEvent
|
||||
* @param ev The event.
|
||||
*/
|
||||
ontimeupdate: ((this: Document, ev: Event) => any) | null;
|
||||
ontouchcancel: ((this: Document, ev: Event) => any) | null;
|
||||
ontouchend: ((this: Document, ev: Event) => any) | null;
|
||||
ontouchmove: ((this: Document, ev: Event) => any) | null;
|
||||
ontouchstart: ((this: Document, ev: Event) => any) | null;
|
||||
ontouchcancel: ((this: Document, ev: TouchEvent) => any) | null;
|
||||
ontouchend: ((this: Document, ev: TouchEvent) => any) | null;
|
||||
ontouchmove: ((this: Document, ev: TouchEvent) => any) | null;
|
||||
ontouchstart: ((this: Document, ev: TouchEvent) => any) | null;
|
||||
onvisibilitychange: (this: Document, ev: Event) => any;
|
||||
/**
|
||||
* Occurs when the volume is changed, or playback is muted or unmuted.
|
||||
@@ -4138,6 +4139,7 @@ interface Document extends Node, GlobalEventHandlers, ParentNode, DocumentEvent
|
||||
* @param y The y-offset
|
||||
*/
|
||||
elementFromPoint(x: number, y: number): Element;
|
||||
elementsFromPoint(x: number, y: number): Element[];
|
||||
evaluate(expression: string, contextNode: Node, resolver: XPathNSResolver | null, type: number, result: XPathResult | null): XPathResult;
|
||||
/**
|
||||
* Executes a command on the current document, current selection, or the given range.
|
||||
@@ -4312,6 +4314,7 @@ interface DocumentEvent {
|
||||
createEvent(eventInterface: "SpeechSynthesisEvent"): SpeechSynthesisEvent;
|
||||
createEvent(eventInterface: "StorageEvent"): StorageEvent;
|
||||
createEvent(eventInterface: "TextEvent"): TextEvent;
|
||||
createEvent(eventInterface: "TouchEvent"): TouchEvent;
|
||||
createEvent(eventInterface: "TrackEvent"): TrackEvent;
|
||||
createEvent(eventInterface: "TransitionEvent"): TransitionEvent;
|
||||
createEvent(eventInterface: "UIEvent"): UIEvent;
|
||||
@@ -4431,10 +4434,10 @@ interface ElementEventMap extends GlobalEventHandlersEventMap {
|
||||
"MSPointerOut": Event;
|
||||
"MSPointerOver": Event;
|
||||
"MSPointerUp": Event;
|
||||
"touchcancel": Event;
|
||||
"touchend": Event;
|
||||
"touchmove": Event;
|
||||
"touchstart": Event;
|
||||
"touchcancel": TouchEvent;
|
||||
"touchend": TouchEvent;
|
||||
"touchmove": TouchEvent;
|
||||
"touchstart": TouchEvent;
|
||||
"webkitfullscreenchange": Event;
|
||||
"webkitfullscreenerror": Event;
|
||||
}
|
||||
@@ -4473,10 +4476,10 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, ParentNod
|
||||
onmspointerout: ((this: Element, ev: Event) => any) | null;
|
||||
onmspointerover: ((this: Element, ev: Event) => any) | null;
|
||||
onmspointerup: ((this: Element, ev: Event) => any) | null;
|
||||
ontouchcancel: ((this: Element, ev: Event) => any) | null;
|
||||
ontouchend: ((this: Element, ev: Event) => any) | null;
|
||||
ontouchmove: ((this: Element, ev: Event) => any) | null;
|
||||
ontouchstart: ((this: Element, ev: Event) => any) | null;
|
||||
ontouchcancel: ((this: Element, ev: TouchEvent) => any) | null;
|
||||
ontouchend: ((this: Element, ev: TouchEvent) => any) | null;
|
||||
ontouchmove: ((this: Element, ev: TouchEvent) => any) | null;
|
||||
ontouchstart: ((this: Element, ev: TouchEvent) => any) | null;
|
||||
onwebkitfullscreenchange: ((this: Element, ev: Event) => any) | null;
|
||||
onwebkitfullscreenerror: ((this: Element, ev: Event) => any) | null;
|
||||
outerHTML: string;
|
||||
@@ -6131,6 +6134,7 @@ interface HTMLImageElement extends HTMLElement {
|
||||
readonly complete: boolean;
|
||||
crossOrigin: string | null;
|
||||
readonly currentSrc: string;
|
||||
decoding: "async" | "sync" | "auto";
|
||||
/**
|
||||
* Sets or retrieves the height of the object.
|
||||
*/
|
||||
@@ -14742,7 +14746,7 @@ interface WebSocket extends EventTarget {
|
||||
readonly readyState: number;
|
||||
readonly url: string;
|
||||
close(code?: number, reason?: string): void;
|
||||
send(data: string | ArrayBuffer | Blob | ArrayBufferView): void;
|
||||
send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;
|
||||
readonly CLOSED: number;
|
||||
readonly CLOSING: number;
|
||||
readonly CONNECTING: number;
|
||||
@@ -14868,10 +14872,10 @@ interface WindowEventMap extends GlobalEventHandlersEventMap {
|
||||
"submit": Event;
|
||||
"suspend": Event;
|
||||
"timeupdate": Event;
|
||||
"touchcancel": Event;
|
||||
"touchend": Event;
|
||||
"touchmove": Event;
|
||||
"touchstart": Event;
|
||||
"touchcancel": TouchEvent;
|
||||
"touchend": TouchEvent;
|
||||
"touchmove": TouchEvent;
|
||||
"touchstart": TouchEvent;
|
||||
"unload": Event;
|
||||
"volumechange": Event;
|
||||
"vrdisplayactivate": Event;
|
||||
@@ -15059,6 +15063,7 @@ interface Window extends EventTarget, WindowTimers, WindowSessionStorage, Window
|
||||
msWriteProfilerMark(profilerMarkName: string): void;
|
||||
open(url?: string, target?: string, features?: string, replace?: boolean): Window | null;
|
||||
postMessage(message: any, targetOrigin: string, transfer?: any[]): void;
|
||||
print(): void;
|
||||
prompt(message?: string, _default?: string): string | null;
|
||||
releaseEvents(): void;
|
||||
requestAnimationFrame(callback: FrameRequestCallback): number;
|
||||
@@ -15868,6 +15873,7 @@ declare function moveTo(x?: number, y?: number): void;
|
||||
declare function msWriteProfilerMark(profilerMarkName: string): void;
|
||||
declare function open(url?: string, target?: string, features?: string, replace?: boolean): Window | null;
|
||||
declare function postMessage(message: any, targetOrigin: string, transfer?: any[]): void;
|
||||
declare function print(): void;
|
||||
declare function prompt(message?: string, _default?: string): string | null;
|
||||
declare function releaseEvents(): void;
|
||||
declare function requestAnimationFrame(callback: FrameRequestCallback): number;
|
||||
|
||||
Vendored
+4
-4
@@ -10,7 +10,7 @@ interface Map<K, V> {
|
||||
|
||||
interface MapConstructor {
|
||||
new (): Map<any, any>;
|
||||
new <K, V>(entries?: ReadonlyArray<[K, V]>): Map<K, V>;
|
||||
new <K, V>(entries?: ReadonlyArray<[K, V]> | null): Map<K, V>;
|
||||
readonly prototype: Map<any, any>;
|
||||
}
|
||||
declare var Map: MapConstructor;
|
||||
@@ -31,7 +31,7 @@ interface WeakMap<K extends object, V> {
|
||||
|
||||
interface WeakMapConstructor {
|
||||
new (): WeakMap<object, any>;
|
||||
new <K extends object, V>(entries?: ReadonlyArray<[K, V]>): WeakMap<K, V>;
|
||||
new <K extends object, V>(entries?: ReadonlyArray<[K, V]> | null): WeakMap<K, V>;
|
||||
readonly prototype: WeakMap<object, any>;
|
||||
}
|
||||
declare var WeakMap: WeakMapConstructor;
|
||||
@@ -47,7 +47,7 @@ interface Set<T> {
|
||||
|
||||
interface SetConstructor {
|
||||
new (): Set<any>;
|
||||
new <T>(values?: ReadonlyArray<T>): Set<T>;
|
||||
new <T>(values?: ReadonlyArray<T> | null): Set<T>;
|
||||
readonly prototype: Set<any>;
|
||||
}
|
||||
declare var Set: SetConstructor;
|
||||
@@ -66,7 +66,7 @@ interface WeakSet<T extends object> {
|
||||
|
||||
interface WeakSetConstructor {
|
||||
new (): WeakSet<object>;
|
||||
new <T extends object>(values?: ReadonlyArray<T>): WeakSet<T>;
|
||||
new <T extends object>(values?: ReadonlyArray<T> | null): WeakSet<T>;
|
||||
readonly prototype: WeakSet<object>;
|
||||
}
|
||||
declare var WeakSet: WeakSetConstructor;
|
||||
|
||||
Vendored
+1
-1
@@ -1443,7 +1443,7 @@ interface WebSocket extends EventTarget {
|
||||
readonly readyState: number;
|
||||
readonly url: string;
|
||||
close(code?: number, reason?: string): void;
|
||||
send(data: string | ArrayBuffer | Blob | ArrayBufferView): void;
|
||||
send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;
|
||||
readonly CLOSED: number;
|
||||
readonly CLOSING: number;
|
||||
readonly CONNECTING: number;
|
||||
|
||||
@@ -557,7 +557,7 @@ namespace ts.server {
|
||||
const request = this.processRequest<protocol.CodeFixRequest>(CommandNames.GetCodeFixes, args);
|
||||
const response = this.processResponse<protocol.CodeFixResponse>(request);
|
||||
|
||||
return response.body.map(({ description, changes, fixId }) => ({ description, changes: this.convertChanges(changes, file), fixId }));
|
||||
return response.body.map(({ description, changes, fixId, fixAllDescription }) => ({ description, changes: this.convertChanges(changes, file), fixId, fixAllDescription }));
|
||||
}
|
||||
|
||||
getCombinedCodeFix = notImplemented;
|
||||
|
||||
@@ -1701,6 +1701,8 @@ namespace ts.server.protocol {
|
||||
* This may be omitted to indicate that the code fix can't be applied in a group.
|
||||
*/
|
||||
fixId?: {};
|
||||
/** Should be present if and only if 'fixId' is. */
|
||||
fixAllDescription?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1723,9 +1723,9 @@ namespace ts.server {
|
||||
return { startPosition, endPosition };
|
||||
}
|
||||
|
||||
private mapCodeAction(project: Project, { description, changes: unmappedChanges, commands, fixId }: CodeFixAction): protocol.CodeFixAction {
|
||||
private mapCodeAction(project: Project, { description, changes: unmappedChanges, commands, fixId, fixAllDescription }: CodeFixAction): protocol.CodeFixAction {
|
||||
const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, project.getScriptInfoForNormalizedPath(toNormalizedPath(change.fileName))));
|
||||
return { description, changes, commands, fixId };
|
||||
return { description, changes, commands, fixId, fixAllDescription };
|
||||
}
|
||||
|
||||
private mapTextChangesToCodeEdits(project: Project, textChanges: ReadonlyArray<FileTextChanges>): protocol.FileCodeEdits[] {
|
||||
|
||||
@@ -27,6 +27,25 @@ namespace ts {
|
||||
const codeFixRegistrations: CodeFixRegistration[][] = [];
|
||||
const fixIdToRegistration = createMap<CodeFixRegistration>();
|
||||
|
||||
type DiagnosticAndArguments = DiagnosticMessage | [DiagnosticMessage, string] | [DiagnosticMessage, string, string];
|
||||
function diagnosticToString(diag: DiagnosticAndArguments): string {
|
||||
return isArray(diag)
|
||||
? formatStringFromArgs(getLocaleSpecificMessage(diag[0]), diag.slice(1) as ReadonlyArray<string>)
|
||||
: getLocaleSpecificMessage(diag);
|
||||
}
|
||||
|
||||
export function createCodeFixActionNoFixId(changes: FileTextChanges[], description: DiagnosticAndArguments) {
|
||||
return createCodeFixActionWorker(diagnosticToString(description), changes, /*fixId*/ undefined, /*fixAllDescription*/ undefined);
|
||||
}
|
||||
|
||||
export function createCodeFixAction(changes: FileTextChanges[], description: DiagnosticAndArguments, fixId: {}, fixAllDescription: DiagnosticAndArguments, command?: CodeActionCommand): CodeFixAction {
|
||||
return createCodeFixActionWorker(diagnosticToString(description), changes, fixId, diagnosticToString(fixAllDescription), command);
|
||||
}
|
||||
|
||||
function createCodeFixActionWorker(description: string, changes: FileTextChanges[], fixId?: {}, fixAllDescription?: string, command?: CodeActionCommand): CodeFixAction {
|
||||
return { description, changes, fixId, fixAllDescription, commands: command ? [command] : undefined };
|
||||
}
|
||||
|
||||
export function registerCodeFix(reg: CodeFixRegistration) {
|
||||
for (const error of reg.errorCodes) {
|
||||
let registrations = codeFixRegistrations[error];
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ts.codefix {
|
||||
errorCodes,
|
||||
getCodeActions: (context) => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Call_decorator_expression, fixId, Diagnostics.Add_to_all_uncalled_decorators)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file!, diag.start!)),
|
||||
|
||||
@@ -7,9 +7,8 @@ namespace ts.codefix {
|
||||
getCodeActions(context) {
|
||||
const decl = getDeclaration(context.sourceFile, context.span.start);
|
||||
if (!decl) return;
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Annotate_with_type_from_JSDoc);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, decl));
|
||||
return [{ description, changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Annotate_with_type_from_JSDoc, fixId, Diagnostics.Annotate_everything_with_types_from_JSDoc)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ts.codefix {
|
||||
errorCodes,
|
||||
getCodeActions(context: CodeFixContext) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.program.getTypeChecker()));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Convert_function_to_an_ES2015_class), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Convert_function_to_an_ES2015_class, fixId, Diagnostics.Convert_all_constructor_functions_to_classes)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, err) => doChange(changes, err.file!, err.start, context.program.getTypeChecker())),
|
||||
|
||||
@@ -3,7 +3,6 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES6_module.code],
|
||||
getCodeActions(context) {
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Convert_to_ES6_module);
|
||||
const { sourceFile, program } = context;
|
||||
const changes = textChanges.ChangeTracker.with(context, changes => {
|
||||
const moduleExportsChangedToDefault = convertFileToEs6Module(sourceFile, program.getTypeChecker(), changes, program.getCompilerOptions().target);
|
||||
@@ -14,14 +13,13 @@ namespace ts.codefix {
|
||||
}
|
||||
});
|
||||
// No support for fix-all since this applies to the whole file at once anyway.
|
||||
return [{ description, changes, fixId: undefined }];
|
||||
return [createCodeFixActionNoFixId(changes, Diagnostics.Convert_to_ES6_module)];
|
||||
},
|
||||
});
|
||||
|
||||
function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: textChanges.ChangeTracker) {
|
||||
for (const moduleSpecifier of importingFile.imports) {
|
||||
const { text } = moduleSpecifier;
|
||||
const imported = getResolvedModule(importingFile, text);
|
||||
const imported = getResolvedModule(importingFile, moduleSpecifier.text);
|
||||
if (!imported || imported.resolvedFileName !== exportingFile.fileName) {
|
||||
continue;
|
||||
}
|
||||
@@ -29,7 +27,7 @@ namespace ts.codefix {
|
||||
const importNode = importFromModuleSpecifier(moduleSpecifier);
|
||||
switch (importNode.kind) {
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
changes.replaceNode(importingFile, importNode, makeImport(importNode.name, /*namedImports*/ undefined, text));
|
||||
changes.replaceNode(importingFile, importNode, makeImport(importNode.name, /*namedImports*/ undefined, moduleSpecifier));
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
if (isRequireCall(importNode, /*checkArgumentIsStringLiteralLike*/ false)) {
|
||||
@@ -111,7 +109,7 @@ namespace ts.codefix {
|
||||
case SyntaxKind.CallExpression: {
|
||||
if (isRequireCall(expression, /*checkArgumentIsStringLiteralLike*/ true)) {
|
||||
// For side-effecting require() call, just make a side-effecting import.
|
||||
changes.replaceNode(sourceFile, statement, makeImport(/*name*/ undefined, /*namedImports*/ undefined, expression.arguments[0].text));
|
||||
changes.replaceNode(sourceFile, statement, makeImport(/*name*/ undefined, /*namedImports*/ undefined, expression.arguments[0]));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -139,11 +137,11 @@ namespace ts.codefix {
|
||||
}
|
||||
if (isRequireCall(initializer, /*checkArgumentIsStringLiteralLike*/ true)) {
|
||||
foundImport = true;
|
||||
return convertSingleImport(sourceFile, name, initializer.arguments[0].text, changes, checker, identifiers, target);
|
||||
return convertSingleImport(sourceFile, name, initializer.arguments[0], changes, checker, identifiers, target);
|
||||
}
|
||||
else if (isPropertyAccessExpression(initializer) && isRequireCall(initializer.expression, /*checkArgumentIsStringLiteralLike*/ true)) {
|
||||
foundImport = true;
|
||||
return convertPropertyAccessImport(name, initializer.name.text, initializer.expression.arguments[0].text, identifiers);
|
||||
return convertPropertyAccessImport(name, initializer.name.text, initializer.expression.arguments[0], identifiers);
|
||||
}
|
||||
else {
|
||||
// Move it out to its own variable statement.
|
||||
@@ -157,7 +155,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
/** Converts `const name = require("moduleSpecifier").propertyName` */
|
||||
function convertPropertyAccessImport(name: BindingName, propertyName: string, moduleSpecifier: string, identifiers: Identifiers): ReadonlyArray<Node> {
|
||||
function convertPropertyAccessImport(name: BindingName, propertyName: string, moduleSpecifier: StringLiteralLike, identifiers: Identifiers): ReadonlyArray<Node> {
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
case SyntaxKind.ArrayBindingPattern: {
|
||||
@@ -257,7 +255,7 @@ namespace ts.codefix {
|
||||
changes.replaceNodeWithNodes(sourceFile, statement, newNodes);
|
||||
}
|
||||
else {
|
||||
changes.replaceNode(sourceFile, statement, convertExportsDotXEquals(text, right), { useNonAdjustedEndPosition: true });
|
||||
changes.replaceNode(sourceFile, statement, convertExportsDotXEquals(text, right));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +338,7 @@ namespace ts.codefix {
|
||||
function convertSingleImport(
|
||||
file: SourceFile,
|
||||
name: BindingName,
|
||||
moduleSpecifier: string,
|
||||
moduleSpecifier: StringLiteralLike,
|
||||
changes: textChanges.ChangeTracker,
|
||||
checker: TypeChecker,
|
||||
identifiers: Identifiers,
|
||||
@@ -362,7 +360,7 @@ namespace ts.codefix {
|
||||
import x from "x";
|
||||
const [a, b, c] = x;
|
||||
*/
|
||||
const tmp = makeUniqueName(moduleSpecifierToValidIdentifier(moduleSpecifier, target), identifiers);
|
||||
const tmp = makeUniqueName(moduleSpecifierToValidIdentifier(moduleSpecifier.text, target), identifiers);
|
||||
return [
|
||||
makeImport(createIdentifier(tmp), /*namedImports*/ undefined, moduleSpecifier),
|
||||
makeConst(/*modifiers*/ undefined, getSynthesizedDeepClone(name), createIdentifier(tmp)),
|
||||
@@ -379,7 +377,7 @@ namespace ts.codefix {
|
||||
* Convert `import x = require("x").`
|
||||
* Also converts uses like `x.y()` to `y()` and uses a named import.
|
||||
*/
|
||||
function convertSingleIdentifierImport(file: SourceFile, name: Identifier, moduleSpecifier: string, changes: textChanges.ChangeTracker, checker: TypeChecker, identifiers: Identifiers): ReadonlyArray<Node> {
|
||||
function convertSingleIdentifierImport(file: SourceFile, name: Identifier, moduleSpecifier: StringLiteralLike, changes: textChanges.ChangeTracker, checker: TypeChecker, identifiers: Identifiers): ReadonlyArray<Node> {
|
||||
const nameSymbol = checker.getSymbolAtLocation(name);
|
||||
// Maps from module property name to name actually used. (The same if there isn't shadowing.)
|
||||
const namedBindingsNames = createMap<string>();
|
||||
@@ -486,14 +484,14 @@ namespace ts.codefix {
|
||||
getSynthesizedDeepClones(cls.members));
|
||||
}
|
||||
|
||||
function makeSingleImport(localName: string, propertyName: string, moduleSpecifier: string): ImportDeclaration {
|
||||
function makeSingleImport(localName: string, propertyName: string, moduleSpecifier: StringLiteralLike): ImportDeclaration {
|
||||
return propertyName === "default"
|
||||
? makeImport(createIdentifier(localName), /*namedImports*/ undefined, moduleSpecifier)
|
||||
: makeImport(/*name*/ undefined, [makeImportSpecifier(propertyName, localName)], moduleSpecifier);
|
||||
}
|
||||
|
||||
function makeImport(name: Identifier | undefined, namedImports: ReadonlyArray<ImportSpecifier> | undefined, moduleSpecifier: string): ImportDeclaration {
|
||||
return makeImportDeclaration(name, namedImports, createLiteral(moduleSpecifier));
|
||||
function makeImport(name: Identifier | undefined, namedImports: ReadonlyArray<ImportSpecifier> | undefined, moduleSpecifier: StringLiteralLike): ImportDeclaration {
|
||||
return makeImportDeclaration(name, namedImports, moduleSpecifier);
|
||||
}
|
||||
|
||||
export function makeImportDeclaration(name: Identifier, namedImports: ReadonlyArray<ImportSpecifier> | undefined, moduleSpecifier: Expression) {
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace ts.codefix {
|
||||
const qualifiedName = getQualifiedName(context.sourceFile, context.span.start);
|
||||
if (!qualifiedName) return undefined;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName));
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${qualifiedName.left.text}["${qualifiedName.right.text}"]`]);
|
||||
return [{ description, changes, fixId }];
|
||||
const newText = `${qualifiedName.left.text}["${qualifiedName.right.text}"]`;
|
||||
return [createCodeFixAction(changes, [Diagnostics.Rewrite_as_the_indexed_access_type_0, newText], fixId, Diagnostics.Rewrite_all_as_indexed_access_types)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: (context) => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
|
||||
@@ -16,23 +16,18 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
const fixes: CodeFixAction[] = [
|
||||
{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Disable_checking_for_this_file),
|
||||
changes: [createFileTextChanges(sourceFile.fileName, [
|
||||
// fixId unnecessary because adding `// @ts-nocheck` even once will ignore every error in the file.
|
||||
createCodeFixActionNoFixId(
|
||||
[createFileTextChanges(sourceFile.fileName, [
|
||||
createTextChange(sourceFile.checkJsDirective
|
||||
? createTextSpanFromBounds(sourceFile.checkJsDirective.pos, sourceFile.checkJsDirective.end)
|
||||
: createTextSpan(0, 0), `// @ts-nocheck${getNewLineOrDefaultFromHost(host, formatContext.options)}`),
|
||||
])],
|
||||
// fixId unnecessary because adding `// @ts-nocheck` even once will ignore every error in the file.
|
||||
fixId: undefined,
|
||||
}];
|
||||
Diagnostics.Disable_checking_for_this_file),
|
||||
];
|
||||
|
||||
if (isValidSuppressLocation(sourceFile, span.start)) {
|
||||
fixes.unshift({
|
||||
description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message),
|
||||
changes: textChanges.ChangeTracker.with(context, t => makeChange(t, sourceFile, span.start)),
|
||||
fixId,
|
||||
});
|
||||
if (textChanges.isValidLocationToAddComment(sourceFile, span.start)) {
|
||||
fixes.unshift(createCodeFixAction(textChanges.ChangeTracker.with(context, t => makeChange(t, sourceFile, span.start)), Diagnostics.Ignore_this_error_message, fixId, Diagnostics.Add_ts_ignore_to_all_error_messages));
|
||||
}
|
||||
|
||||
return fixes;
|
||||
@@ -41,37 +36,18 @@ namespace ts.codefix {
|
||||
getAllCodeActions: context => {
|
||||
const seenLines = createMap<true>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
if (isValidSuppressLocation(diag.file!, diag.start!)) {
|
||||
if (textChanges.isValidLocationToAddComment(diag.file!, diag.start!)) {
|
||||
makeChange(changes, diag.file!, diag.start!, seenLines);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function isValidSuppressLocation(sourceFile: SourceFile, position: number) {
|
||||
return !isInComment(sourceFile, position) && !isInString(sourceFile, position) && !isInTemplateString(sourceFile, position);
|
||||
}
|
||||
|
||||
function makeChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, position: number, seenLines?: Map<true>) {
|
||||
const { line: lineNumber } = getLineAndCharacterOfPosition(sourceFile, position);
|
||||
|
||||
// Only need to add `// @ts-ignore` for a line once.
|
||||
if (seenLines && !addToSeen(seenLines, lineNumber)) {
|
||||
return;
|
||||
if (!seenLines || addToSeen(seenLines, lineNumber)) {
|
||||
changes.insertCommentBeforeLine(sourceFile, lineNumber, position, " @ts-ignore");
|
||||
}
|
||||
|
||||
const lineStartPosition = getStartPositionOfLine(lineNumber, sourceFile);
|
||||
const startPosition = getFirstNonSpaceCharacterPosition(sourceFile.text, lineStartPosition);
|
||||
|
||||
// First try to see if we can put the '// @ts-ignore' on the previous line.
|
||||
// We need to make sure that we are not in the middle of a string literal or a comment.
|
||||
// If so, we do not want to separate the node from its comment if we can.
|
||||
// Otherwise, add an extra new line immediately before the error span.
|
||||
const insertAtLineStart = isValidSuppressLocation(sourceFile, startPosition);
|
||||
|
||||
const token = getTouchingToken(sourceFile, insertAtLineStart ? startPosition : position, /*includeJsDocComment*/ false);
|
||||
const clone = setStartsOnNewLine(getSynthesizedDeepClone(token), true);
|
||||
addSyntheticLeadingComment(clone, SyntaxKind.SingleLineCommentTrivia, " @ts-ignore");
|
||||
changes.replaceNode(sourceFile, token, clone, { preserveLeadingWhitespace: true, prefix: insertAtLineStart ? undefined : changes.newLineCharacter });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace ts.codefix {
|
||||
},
|
||||
});
|
||||
|
||||
interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression; }
|
||||
interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
|
||||
function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined {
|
||||
// The identifier of the missing property. eg:
|
||||
// this.missing = 1;
|
||||
@@ -56,50 +56,26 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const classAndMakeStatic = getClassAndMakeStatic(token, checker);
|
||||
if (!classAndMakeStatic) {
|
||||
return undefined;
|
||||
}
|
||||
const { classDeclaration, makeStatic } = classAndMakeStatic;
|
||||
const { parent } = token;
|
||||
if (!isPropertyAccessExpression(parent)) return undefined;
|
||||
|
||||
const leftExpressionType = skipConstraint(checker.getTypeAtLocation(parent.expression));
|
||||
const { symbol } = leftExpressionType;
|
||||
const classDeclaration = symbol && symbol.declarations && find(symbol.declarations, isClassLike);
|
||||
if (!classDeclaration) return undefined;
|
||||
|
||||
const makeStatic = (leftExpressionType as TypeReference).target !== checker.getDeclaredTypeOfSymbol(symbol);
|
||||
const classDeclarationSourceFile = classDeclaration.getSourceFile();
|
||||
const inJs = isInJavaScriptFile(classDeclarationSourceFile);
|
||||
const call = tryCast(token.parent.parent, isCallExpression);
|
||||
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
|
||||
const call = tryCast(parent.parent, isCallExpression);
|
||||
|
||||
return { token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
|
||||
}
|
||||
|
||||
function getClassAndMakeStatic(token: Node, checker: TypeChecker): { readonly classDeclaration: ClassLikeDeclaration, readonly makeStatic: boolean } | undefined {
|
||||
const { parent } = token;
|
||||
if (!isPropertyAccessExpression(parent)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (parent.expression.kind === SyntaxKind.ThisKeyword) {
|
||||
const containingClassMemberDeclaration = getThisContainer(token, /*includeArrowFunctions*/ false);
|
||||
if (!isClassElement(containingClassMemberDeclaration)) {
|
||||
return undefined;
|
||||
}
|
||||
const classDeclaration = containingClassMemberDeclaration.parent;
|
||||
// Property accesses on `this` in a static method are accesses of a static member.
|
||||
return isClassLike(classDeclaration) ? { classDeclaration, makeStatic: hasModifier(containingClassMemberDeclaration, ModifierFlags.Static) } : undefined;
|
||||
}
|
||||
else {
|
||||
const leftExpressionType = checker.getTypeAtLocation(parent.expression);
|
||||
const { symbol } = leftExpressionType;
|
||||
if (!(symbol && leftExpressionType.flags & TypeFlags.Object && symbol.flags & SymbolFlags.Class)) {
|
||||
return undefined;
|
||||
}
|
||||
const classDeclaration = cast(first(symbol.declarations), isClassLike);
|
||||
// The expression is a class symbol but the type is not the instance-side.
|
||||
return { classDeclaration, makeStatic: leftExpressionType !== checker.getDeclaredTypeOfSymbol(symbol) };
|
||||
}
|
||||
}
|
||||
|
||||
function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic));
|
||||
if (changes.length === 0) return undefined;
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]);
|
||||
return { description, changes, fixId };
|
||||
return changes.length === 0 ? undefined
|
||||
: createCodeFixAction(changes, [makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor, tokenName], fixId, Diagnostics.Add_all_missing_members);
|
||||
}
|
||||
|
||||
function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): void {
|
||||
@@ -143,9 +119,8 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic));
|
||||
return { description, changes, fixId };
|
||||
return createCodeFixAction(changes, [makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, tokenName], fixId, Diagnostics.Add_all_missing_members);
|
||||
}
|
||||
|
||||
function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode, makeStatic: boolean): void {
|
||||
@@ -178,7 +153,7 @@ namespace ts.codefix {
|
||||
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature));
|
||||
// No fixId here because code-fix-all currently only works on adding individual named properties.
|
||||
return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined };
|
||||
return createCodeFixActionNoFixId(changes, [Diagnostics.Add_index_signature_for_property_0, tokenName]);
|
||||
}
|
||||
|
||||
function getActionForMethodDeclaration(
|
||||
@@ -191,9 +166,8 @@ namespace ts.codefix {
|
||||
inJs: boolean,
|
||||
preferences: UserPreferences,
|
||||
): CodeFixAction | undefined {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs, preferences));
|
||||
return { description, changes, fixId };
|
||||
return createCodeFixAction(changes, [makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, token.text], fixId, Diagnostics.Add_all_missing_members);
|
||||
}
|
||||
|
||||
function addMethodDeclaration(
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace ts.codefix {
|
||||
const nodes = getNodes(sourceFile, span.start);
|
||||
if (!nodes) return undefined;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Add_async_modifier_to_containing_function), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
|
||||
@@ -5,38 +5,27 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: context => {
|
||||
const codeAction = tryGetCodeActionForInstallPackageTypes(context.host, context.sourceFile.fileName, getModuleName(context.sourceFile, context.span.start));
|
||||
return codeAction && [{ fixId, ...codeAction }];
|
||||
const { host, sourceFile, span: { start } } = context;
|
||||
const packageName = getTypesPackageNameToInstall(host, sourceFile, start);
|
||||
return packageName === undefined ? []
|
||||
: [createCodeFixAction(/*changes*/ [], [Diagnostics.Install_0, packageName], fixId, Diagnostics.Install_all_missing_types_packages, getCommand(sourceFile.fileName, packageName))];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (_, diag, commands) => {
|
||||
const pkg = getTypesPackageNameToInstall(context.host, getModuleName(diag.file, diag.start));
|
||||
const pkg = getTypesPackageNameToInstall(context.host, diag.file, diag.start);
|
||||
if (pkg) {
|
||||
commands.push(getCommand(diag.file.fileName, pkg));
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
function getModuleName(sourceFile: SourceFile, pos: number): string {
|
||||
return cast(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false), isStringLiteral).text;
|
||||
}
|
||||
|
||||
function getCommand(fileName: string, packageName: string): InstallPackageAction {
|
||||
return { type: "install package", file: fileName, packageName };
|
||||
}
|
||||
|
||||
function getTypesPackageNameToInstall(host: LanguageServiceHost, moduleName: string): string | undefined {
|
||||
function getTypesPackageNameToInstall(host: LanguageServiceHost, sourceFile: SourceFile, pos: number): string | undefined {
|
||||
const moduleName = cast(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false), isStringLiteral).text;
|
||||
const { packageName } = getPackageName(moduleName);
|
||||
// If !registry, registry not available yet, can't do anything.
|
||||
return host.isKnownTypesPackageName(packageName) ? getTypesPackageName(packageName) : undefined;
|
||||
}
|
||||
|
||||
function tryGetCodeActionForInstallPackageTypes(host: LanguageServiceHost, fileName: string, moduleName: string): CodeAction | undefined {
|
||||
const packageName = getTypesPackageNameToInstall(host, moduleName);
|
||||
return packageName === undefined ? undefined : {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Install_0), [packageName]),
|
||||
changes: [],
|
||||
commands: [getCommand(fileName, packageName)],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace ts.codefix {
|
||||
const { program, sourceFile, span } = context;
|
||||
const changes = textChanges.ChangeTracker.with(context, t =>
|
||||
addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t, context.preferences));
|
||||
return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }];
|
||||
return changes.length === 0 ? undefined : [createCodeFixAction(changes, Diagnostics.Implement_inherited_abstract_class, fixId, Diagnostics.Implement_all_inherited_abstract_classes)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => {
|
||||
|
||||
@@ -11,9 +11,7 @@ namespace ts.codefix {
|
||||
const checker = program.getTypeChecker();
|
||||
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences));
|
||||
if (changes.length === 0) return undefined;
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]);
|
||||
return { description, changes, fixId };
|
||||
return changes.length === 0 ? undefined : createCodeFixAction(changes, [Diagnostics.Implement_interface_0, implementedTypeNode.getText(sourceFile)], fixId, Diagnostics.Implement_all_unimplemented_interfaces);
|
||||
});
|
||||
},
|
||||
fixIds: [fixId],
|
||||
@@ -50,10 +48,10 @@ namespace ts.codefix {
|
||||
|
||||
const classType = checker.getTypeAtLocation(classDeclaration);
|
||||
|
||||
if (!checker.getIndexTypeOfType(classType, IndexKind.Number)) {
|
||||
if (!classType.getNumberIndexType()) {
|
||||
createMissingIndexSignatureDeclaration(implementedType, IndexKind.Number);
|
||||
}
|
||||
if (!checker.getIndexTypeOfType(classType, IndexKind.String)) {
|
||||
if (!classType.getStringIndexType()) {
|
||||
createMissingIndexSignatureDeclaration(implementedType, IndexKind.String);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ts.codefix {
|
||||
if (!nodes) return undefined;
|
||||
const { constructor, superCall } = nodes;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Make_super_call_the_first_statement_in_the_constructor, fixId, Diagnostics.Make_all_super_calls_the_first_statement_in_their_constructor)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions(context) {
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ts.codefix {
|
||||
const { sourceFile, span } = context;
|
||||
const ctr = getNode(sourceFile, span.start);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Add_missing_super_call, fixId, Diagnostics.Add_all_missing_super_calls)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) =>
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ts.codefix {
|
||||
if (!nodes) return undefined;
|
||||
const { extendsToken, heritageClauses } = nodes;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChanges(t, sourceFile, extendsToken, heritageClauses));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Change_extends_to_implements, fixId, Diagnostics.Change_all_extended_interfaces_to_implements)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
@@ -27,7 +27,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function doChanges(changes: textChanges.ChangeTracker, sourceFile: SourceFile, extendsToken: Node, heritageClauses: ReadonlyArray<HeritageClause>): void {
|
||||
changes.replaceNode(sourceFile, extendsToken, createToken(SyntaxKind.ImplementsKeyword), textChanges.useNonAdjustedPositions);
|
||||
changes.replaceNode(sourceFile, extendsToken, createToken(SyntaxKind.ImplementsKeyword));
|
||||
|
||||
// If there is already an implements clause, replace the implements keyword with a comma.
|
||||
if (heritageClauses.length === 2 &&
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, token));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Add_this_to_unresolved_variable), changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Add_this_to_unresolved_variable, fixId, Diagnostics.Add_this_to_all_unresolved_variables_matching_a_member_name)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
@@ -30,6 +30,6 @@ namespace ts.codefix {
|
||||
}
|
||||
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): use shared helper
|
||||
suppressLeadingAndTrailingTrivia(token);
|
||||
changes.replaceNode(sourceFile, token, createPropertyAccess(createThis(), token), textChanges.useNonAdjustedPositions);
|
||||
changes.replaceNode(sourceFile, token, createPropertyAccess(createThis(), token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ts.codefix {
|
||||
getCodeActions: getActionsForInvalidImport
|
||||
});
|
||||
|
||||
function getActionsForInvalidImport(context: CodeFixContext): CodeAction[] | undefined {
|
||||
function getActionsForInvalidImport(context: CodeFixContext): CodeFixAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
|
||||
// This is the whole import statement, eg:
|
||||
@@ -19,11 +19,11 @@ namespace ts.codefix {
|
||||
return getCodeFixesForImportDeclaration(context, node);
|
||||
}
|
||||
|
||||
function getCodeFixesForImportDeclaration(context: CodeFixContext, node: ImportDeclaration) {
|
||||
function getCodeFixesForImportDeclaration(context: CodeFixContext, node: ImportDeclaration): CodeFixAction[] {
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
const namespace = getNamespaceDeclarationNode(node) as NamespaceImport;
|
||||
const opts = context.program.getCompilerOptions();
|
||||
const variations: CodeAction[] = [];
|
||||
const variations: CodeFixAction[] = [];
|
||||
|
||||
// import Bluebird from "bluebird";
|
||||
variations.push(createAction(context, sourceFile, node, makeImportDeclaration(namespace.name, /*namedImports*/ undefined, node.moduleSpecifier)));
|
||||
@@ -41,13 +41,9 @@ namespace ts.codefix {
|
||||
return variations;
|
||||
}
|
||||
|
||||
function createAction(context: CodeFixContext, sourceFile: SourceFile, node: Node, replacement: Node): CodeAction {
|
||||
// TODO: GH#21246 Should be able to use `replaceNode`, but be sure to preserve comments (see `codeFixCalledES2015Import11.ts`)
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.replaceRange(sourceFile, { pos: node.getStart(), end: node.end }, replacement));
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Replace_import_with_0), [changes[0].textChanges[0].newText]),
|
||||
changes,
|
||||
};
|
||||
function createAction(context: CodeFixContext, sourceFile: SourceFile, node: Node, replacement: Node): CodeFixAction {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.replaceNode(sourceFile, node, replacement));
|
||||
return createCodeFixActionNoFixId(changes, [Diagnostics.Replace_import_with_0, changes[0].textChanges[0].newText]);
|
||||
}
|
||||
|
||||
registerCodeFix({
|
||||
@@ -58,7 +54,7 @@ namespace ts.codefix {
|
||||
getCodeActions: getActionsForUsageOfInvalidImport
|
||||
});
|
||||
|
||||
function getActionsForUsageOfInvalidImport(context: CodeFixContext): CodeAction[] | undefined {
|
||||
function getActionsForUsageOfInvalidImport(context: CodeFixContext): CodeFixAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const targetKind = Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures.code === context.errorCode ? SyntaxKind.CallExpression : SyntaxKind.NewExpression;
|
||||
const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false), a => a.kind === targetKind && a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)) as CallExpression | NewExpression;
|
||||
@@ -70,15 +66,13 @@ namespace ts.codefix {
|
||||
if (!(type.symbol && (type.symbol as TransientSymbol).originatingImport)) {
|
||||
return [];
|
||||
}
|
||||
const fixes: CodeAction[] = [];
|
||||
const fixes: CodeFixAction[] = [];
|
||||
const relatedImport = (type.symbol as TransientSymbol).originatingImport;
|
||||
if (!isImportCall(relatedImport)) {
|
||||
addRange(fixes, getCodeFixesForImportDeclaration(context, relatedImport));
|
||||
}
|
||||
fixes.push({
|
||||
description: getLocaleSpecificMessage(Diagnostics.Use_synthetic_default_member),
|
||||
changes: textChanges.ChangeTracker.with(context, t => t.replaceNode(sourceFile, expr, createPropertyAccess(expr, "default"), {})),
|
||||
});
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.replaceNode(sourceFile, expr, createPropertyAccess(expr, "default"), {}));
|
||||
fixes.push(createCodeFixActionNoFixId(changes, Diagnostics.Use_synthetic_default_member));
|
||||
return fixes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,17 @@ namespace ts.codefix {
|
||||
if (!info) return undefined;
|
||||
const { typeNode, type } = info;
|
||||
const original = typeNode.getText(sourceFile);
|
||||
const actions = [fix(type, fixIdPlain)];
|
||||
const actions = [fix(type, fixIdPlain, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript)];
|
||||
if (typeNode.kind === SyntaxKind.JSDocNullableType) {
|
||||
// for nullable types, suggest the flow-compatible `T | null | undefined`
|
||||
// in addition to the jsdoc/closure-compatible `T | null`
|
||||
actions.push(fix(checker.getNullableType(type, TypeFlags.Undefined), fixIdNullable));
|
||||
actions.push(fix(checker.getNullableType(type, TypeFlags.Undefined), fixIdNullable, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript_and_add_undefined_to_nullable_types));
|
||||
}
|
||||
return actions;
|
||||
|
||||
function fix(type: Type, fixId: string): CodeFixAction {
|
||||
const newText = checker.typeToString(type);
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [original, newText]);
|
||||
function fix(type: Type, fixId: string, fixAllDescription: DiagnosticMessage): CodeFixAction {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, typeNode, type, checker));
|
||||
return { description, changes, fixId };
|
||||
return createCodeFixAction(changes, [Diagnostics.Change_0_to_1, original, checker.typeToString(type)], fixId, fixAllDescription);
|
||||
}
|
||||
},
|
||||
fixIds: [fixIdPlain, fixIdNullable],
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace ts.codefix {
|
||||
if (!info) return undefined;
|
||||
const { node, suggestion } = info;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion));
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_spelling_to_0), [suggestion]);
|
||||
return [{ description, changes, fixId }];
|
||||
return [createCodeFixAction(changes, [Diagnostics.Change_spelling_to_0, suggestion], fixId, Diagnostics.Fix_all_detected_spelling_errors)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
|
||||
@@ -10,27 +10,24 @@ namespace ts.codefix {
|
||||
const propertyDeclaration = getPropertyDeclaration(context.sourceFile, context.span.start);
|
||||
if (!propertyDeclaration) return;
|
||||
|
||||
const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options);
|
||||
const result = [
|
||||
getActionForAddMissingUndefinedType(context, propertyDeclaration),
|
||||
getActionForAddMissingDefiniteAssignmentAssertion(context, propertyDeclaration, newLineCharacter)
|
||||
getActionForAddMissingDefiniteAssignmentAssertion(context, propertyDeclaration)
|
||||
];
|
||||
|
||||
append(result, getActionForAddMissingInitializer(context, propertyDeclaration, newLineCharacter));
|
||||
append(result, getActionForAddMissingInitializer(context, propertyDeclaration));
|
||||
|
||||
return result;
|
||||
},
|
||||
fixIds: [fixIdAddDefiniteAssignmentAssertions, fixIdAddUndefinedType, fixIdAddInitializer],
|
||||
getAllCodeActions: context => {
|
||||
const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options);
|
||||
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const propertyDeclaration = getPropertyDeclaration(diag.file, diag.start);
|
||||
if (!propertyDeclaration) return;
|
||||
|
||||
switch (context.fixId) {
|
||||
case fixIdAddDefiniteAssignmentAssertions:
|
||||
addDefiniteAssignmentAssertion(changes, diag.file, propertyDeclaration, newLineCharacter);
|
||||
addDefiniteAssignmentAssertion(changes, diag.file, propertyDeclaration);
|
||||
break;
|
||||
case fixIdAddUndefinedType:
|
||||
addUndefinedType(changes, diag.file, propertyDeclaration);
|
||||
@@ -40,7 +37,7 @@ namespace ts.codefix {
|
||||
const initializer = getInitializer(checker, propertyDeclaration);
|
||||
if (!initializer) return;
|
||||
|
||||
addInitializer(changes, diag.file, propertyDeclaration, initializer, newLineCharacter);
|
||||
addInitializer(changes, diag.file, propertyDeclaration, initializer);
|
||||
break;
|
||||
default:
|
||||
Debug.fail(JSON.stringify(context.fixId));
|
||||
@@ -54,13 +51,12 @@ namespace ts.codefix {
|
||||
return isIdentifier(token) ? cast(token.parent, isPropertyDeclaration) : undefined;
|
||||
}
|
||||
|
||||
function getActionForAddMissingDefiniteAssignmentAssertion (context: CodeFixContext, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): CodeFixAction {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_definite_assignment_assertion_to_property_0), [propertyDeclaration.getText()]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addDefiniteAssignmentAssertion(t, context.sourceFile, propertyDeclaration, newLineCharacter));
|
||||
return { description, changes, fixId: fixIdAddDefiniteAssignmentAssertions };
|
||||
function getActionForAddMissingDefiniteAssignmentAssertion (context: CodeFixContext, propertyDeclaration: PropertyDeclaration): CodeFixAction {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addDefiniteAssignmentAssertion(t, context.sourceFile, propertyDeclaration));
|
||||
return createCodeFixAction(changes, [Diagnostics.Add_definite_assignment_assertion_to_property_0, propertyDeclaration.getText()], fixIdAddDefiniteAssignmentAssertions, Diagnostics.Add_definite_assignment_assertions_to_all_uninitialized_properties);
|
||||
}
|
||||
|
||||
function addDefiniteAssignmentAssertion(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): void {
|
||||
function addDefiniteAssignmentAssertion(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration): void {
|
||||
const property = updateProperty(
|
||||
propertyDeclaration,
|
||||
propertyDeclaration.decorators,
|
||||
@@ -70,13 +66,12 @@ namespace ts.codefix {
|
||||
propertyDeclaration.type,
|
||||
propertyDeclaration.initializer
|
||||
);
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration, property, { suffix: newLineCharacter });
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration, property);
|
||||
}
|
||||
|
||||
function getActionForAddMissingUndefinedType (context: CodeFixContext, propertyDeclaration: PropertyDeclaration): CodeFixAction {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_undefined_type_to_property_0), [propertyDeclaration.name.getText()]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addUndefinedType(t, context.sourceFile, propertyDeclaration));
|
||||
return { description, changes, fixId: fixIdAddUndefinedType };
|
||||
return createCodeFixAction(changes, [Diagnostics.Add_undefined_type_to_property_0, propertyDeclaration.name.getText()], fixIdAddUndefinedType, Diagnostics.Add_undefined_type_to_all_uninitialized_properties);
|
||||
}
|
||||
|
||||
function addUndefinedType(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration): void {
|
||||
@@ -85,17 +80,16 @@ namespace ts.codefix {
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration.type, createUnionTypeNode(types));
|
||||
}
|
||||
|
||||
function getActionForAddMissingInitializer (context: CodeFixContext, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): CodeFixAction | undefined {
|
||||
function getActionForAddMissingInitializer(context: CodeFixContext, propertyDeclaration: PropertyDeclaration): CodeFixAction | undefined {
|
||||
const checker = context.program.getTypeChecker();
|
||||
const initializer = getInitializer(checker, propertyDeclaration);
|
||||
if (!initializer) return undefined;
|
||||
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_initializer_to_property_0), [propertyDeclaration.name.getText()]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addInitializer(t, context.sourceFile, propertyDeclaration, initializer, newLineCharacter));
|
||||
return { description, changes, fixId: fixIdAddInitializer };
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addInitializer(t, context.sourceFile, propertyDeclaration, initializer));
|
||||
return createCodeFixAction(changes, [Diagnostics.Add_initializer_to_property_0, propertyDeclaration.name.getText()], fixIdAddInitializer, Diagnostics.Add_initializers_to_all_uninitialized_properties);
|
||||
}
|
||||
|
||||
function addInitializer (changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, initializer: Expression, newLineCharacter: string): void {
|
||||
function addInitializer (changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, initializer: Expression): void {
|
||||
const property = updateProperty(
|
||||
propertyDeclaration,
|
||||
propertyDeclaration.decorators,
|
||||
@@ -105,7 +99,7 @@ namespace ts.codefix {
|
||||
propertyDeclaration.type,
|
||||
initializer
|
||||
);
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration, property, { suffix: newLineCharacter });
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration, property);
|
||||
}
|
||||
|
||||
function getInitializer(checker: TypeChecker, propertyDeclaration: PropertyDeclaration): Expression | undefined {
|
||||
|
||||
@@ -13,9 +13,8 @@ namespace ts.codefix {
|
||||
const { errorCode, sourceFile } = context;
|
||||
const importDecl = tryGetFullImport(sourceFile, context.span.start);
|
||||
if (importDecl) {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_import_from_0), [showModuleSpecifier(importDecl)]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.deleteNode(sourceFile, importDecl));
|
||||
return [{ description, changes, fixId: fixIdDelete }];
|
||||
return [createCodeFixAction(changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDelete, Diagnostics.Delete_all_unused_declarations)];
|
||||
}
|
||||
|
||||
const token = getToken(sourceFile, textSpanEnd(context.span));
|
||||
@@ -23,14 +22,12 @@ namespace ts.codefix {
|
||||
|
||||
const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(t, sourceFile, token));
|
||||
if (deletion.length) {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), [token.getText()]);
|
||||
result.push({ description, changes: deletion, fixId: fixIdDelete });
|
||||
result.push(createCodeFixAction(deletion, [Diagnostics.Remove_declaration_for_Colon_0, token.getText(sourceFile)], fixIdDelete, Diagnostics.Delete_all_unused_declarations));
|
||||
}
|
||||
|
||||
const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, errorCode, sourceFile, token));
|
||||
if (prefix.length) {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Prefix_0_with_an_underscore), [token.getText()]);
|
||||
result.push({ description, changes: prefix, fixId: fixIdPrefix });
|
||||
result.push(createCodeFixAction(prefix, [Diagnostics.Prefix_0_with_an_underscore, token.getText(sourceFile)], fixIdPrefix, Diagnostics.Prefix_all_unused_declarations_with_where_possible));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -165,7 +162,7 @@ namespace ts.codefix {
|
||||
// and trailing trivia will remain.
|
||||
suppressLeadingAndTrailingTrivia(newFunction);
|
||||
|
||||
changes.replaceNode(sourceFile, oldFunction, newFunction, textChanges.useNonAdjustedPositions);
|
||||
changes.replaceNode(sourceFile, oldFunction, newFunction);
|
||||
}
|
||||
else {
|
||||
changes.deleteNodeInList(sourceFile, parent);
|
||||
|
||||
@@ -33,10 +33,9 @@ namespace ts.codefix {
|
||||
preferences: UserPreferences;
|
||||
}
|
||||
|
||||
function createCodeAction(descriptionDiagnostic: DiagnosticMessage, diagnosticArgs: string[], changes: FileTextChanges[]): CodeFixAction {
|
||||
const description = formatMessage.apply(undefined, [undefined, descriptionDiagnostic].concat(<any[]>diagnosticArgs));
|
||||
function createCodeAction(descriptionDiagnostic: DiagnosticMessage, diagnosticArgs: [string, string], changes: FileTextChanges[]): CodeFixAction {
|
||||
// TODO: GH#20315
|
||||
return { description, changes, fixId: undefined };
|
||||
return createCodeFixActionNoFixId(changes, [descriptionDiagnostic, ...diagnosticArgs] as [DiagnosticMessage, string, string]);
|
||||
}
|
||||
|
||||
function convertToImportCodeFixContext(context: CodeFixContext, symbolToken: Node, symbolName: string): ImportCodeFixContext {
|
||||
@@ -640,13 +639,13 @@ namespace ts.codefix {
|
||||
return createCodeAction(Diagnostics.Change_0_to_1, [symbolName, `${namespacePrefix}.${symbolName}`], changes);
|
||||
}
|
||||
|
||||
function getImportCodeActions(context: CodeFixContext): CodeAction[] {
|
||||
function getImportCodeActions(context: CodeFixContext): CodeFixAction[] {
|
||||
return context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
|
||||
? getActionsForUMDImport(context)
|
||||
: getActionsForNonUMDImport(context);
|
||||
}
|
||||
|
||||
function getActionsForUMDImport(context: CodeFixContext): CodeAction[] {
|
||||
function getActionsForUMDImport(context: CodeFixContext): CodeFixAction[] {
|
||||
const token = getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false);
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
@@ -702,7 +701,7 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] | undefined {
|
||||
function getActionsForNonUMDImport(context: CodeFixContext): CodeFixAction[] | undefined {
|
||||
// This will always be an Identifier, since the diagnostics we fix only fail on identifiers.
|
||||
const { sourceFile, span, program, cancellationToken } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
|
||||
@@ -33,10 +33,8 @@ namespace ts.codefix {
|
||||
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
|
||||
let declaration!: Declaration;
|
||||
const changes = textChanges.ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken); });
|
||||
if (changes.length === 0) return undefined;
|
||||
const name = getNameOfDeclaration(declaration).getText();
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(getDiagnostic(errorCode, token)), [name]);
|
||||
return [{ description, changes, fixId }];
|
||||
return changes.length === 0 ? undefined
|
||||
: [createCodeFixAction(changes, [getDiagnostic(errorCode, token), getNameOfDeclaration(declaration).getText(sourceFile)], fixId, Diagnostics.Infer_all_types_from_usage)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions(context) {
|
||||
@@ -60,7 +58,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, seenFunctions?: Map<true>): Declaration | undefined {
|
||||
if (!isAllowedTokenKind(token.kind)) {
|
||||
if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -127,20 +125,6 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function isAllowedTokenKind(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.DotDotDotToken:
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function annotateVariableDeclaration(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, program: Program, cancellationToken: CancellationToken): void {
|
||||
if (isIdentifier(declaration.name)) {
|
||||
annotate(changes, sourceFile, declaration, inferTypeForVariableFromUsage(declaration.name, program, cancellationToken), program);
|
||||
|
||||
@@ -8,9 +8,8 @@ namespace ts.codefix {
|
||||
const { sourceFile, span: { start } } = context;
|
||||
const info = getInfo(sourceFile, start);
|
||||
if (!info) return undefined;
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Convert_to_default_import);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info));
|
||||
return [{ description, changes, fixId }];
|
||||
return [createCodeFixAction(changes, Diagnostics.Convert_to_default_import, fixId, Diagnostics.Convert_all_to_default_imports)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
@@ -38,6 +37,6 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info): void {
|
||||
changes.replaceNode(sourceFile, info.importNode, makeImportDeclaration(info.name, /*namedImports*/ undefined, info.moduleSpecifier), textChanges.useNonAdjustedPositions);
|
||||
changes.replaceNode(sourceFile, info.importNode, makeImportDeclaration(info.name, /*namedImports*/ undefined, info.moduleSpecifier));
|
||||
}
|
||||
}
|
||||
|
||||
+124
-163
@@ -86,11 +86,11 @@ namespace ts.Completions {
|
||||
case StringLiteralCompletionKind.Properties: {
|
||||
const entries: CompletionEntry[] = [];
|
||||
getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, preferences); // Target will not be used, so arbitrary
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries };
|
||||
}
|
||||
case StringLiteralCompletionKind.Types: {
|
||||
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.variableElement, sortText: "0" }));
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
|
||||
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.typeElement, sortText: "0" }));
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
|
||||
}
|
||||
default:
|
||||
return Debug.assertNever(completion);
|
||||
@@ -360,9 +360,14 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
const enum StringLiteralCompletionKind { Paths, Properties, Types }
|
||||
interface StringLiteralCompletionsFromProperties {
|
||||
readonly kind: StringLiteralCompletionKind.Properties;
|
||||
readonly symbols: ReadonlyArray<Symbol>;
|
||||
readonly hasIndexSignature: boolean;
|
||||
}
|
||||
type StringLiteralCompletion =
|
||||
| { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: ReadonlyArray<PathCompletions.PathCompletion> }
|
||||
| { readonly kind: StringLiteralCompletionKind.Properties, readonly symbols: ReadonlyArray<Symbol> }
|
||||
| StringLiteralCompletionsFromProperties
|
||||
| { readonly kind: StringLiteralCompletionKind.Types, readonly types: ReadonlyArray<StringLiteralType> };
|
||||
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost): StringLiteralCompletion | undefined {
|
||||
switch (node.parent.kind) {
|
||||
@@ -377,7 +382,7 @@ namespace ts.Completions {
|
||||
// bar: string;
|
||||
// }
|
||||
// let x: Foo["/*completion position*/"]
|
||||
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType).getApparentProperties() };
|
||||
return stringLiteralCompletionsFromProperties(typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -396,8 +401,7 @@ namespace ts.Completions {
|
||||
// foo({
|
||||
// '/*completion position*/'
|
||||
// });
|
||||
const type = typeChecker.getContextualType(node.parent.parent);
|
||||
return { kind: StringLiteralCompletionKind.Properties, symbols: type && type.getApparentProperties() };
|
||||
return stringLiteralCompletionsFromProperties(typeChecker.getContextualType(node.parent.parent));
|
||||
}
|
||||
return fromContextualType();
|
||||
|
||||
@@ -410,7 +414,7 @@ namespace ts.Completions {
|
||||
// }
|
||||
// let a: A;
|
||||
// a['/*completion position*/']
|
||||
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeAtLocation(expression).getApparentProperties() };
|
||||
return stringLiteralCompletionsFromProperties(typeChecker.getTypeAtLocation(expression));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -454,13 +458,16 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralTypes(type: Type, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> {
|
||||
if (type && type.flags & TypeFlags.TypeParameter) {
|
||||
type = type.getConstraint();
|
||||
}
|
||||
return type && type.flags & TypeFlags.Union
|
||||
function stringLiteralCompletionsFromProperties(type: Type | undefined): StringLiteralCompletionsFromProperties | undefined {
|
||||
return type && { kind: StringLiteralCompletionKind.Properties, symbols: type.getApparentProperties(), hasIndexSignature: hasIndexSignature(type) };
|
||||
}
|
||||
|
||||
function getStringLiteralTypes(type: Type | undefined, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> | undefined {
|
||||
if (!type) return emptyArray;
|
||||
type = skipConstraint(type);
|
||||
return type.flags & TypeFlags.Union
|
||||
? flatMap((<UnionType>type).types, t => getStringLiteralTypes(t, typeChecker, uniques))
|
||||
: type && type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value)
|
||||
: type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value)
|
||||
? [type as StringLiteralType]
|
||||
: emptyArray;
|
||||
}
|
||||
@@ -531,6 +538,15 @@ namespace ts.Completions {
|
||||
): CompletionEntryDetails {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const { name } = entryId;
|
||||
|
||||
const contextToken = findPrecedingToken(position, sourceFile);
|
||||
if (isInString(sourceFile, position, contextToken)) {
|
||||
const stringLiteralCompletions = !contextToken || !isStringLiteralLike(contextToken)
|
||||
? undefined
|
||||
: getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host);
|
||||
return stringLiteralCompletions && stringLiteralCompletionDetails(name, contextToken, stringLiteralCompletions, sourceFile, typeChecker);
|
||||
}
|
||||
|
||||
// Compute all the completion symbols again.
|
||||
const symbolCompletion = getSymbolCompletionFromEntryId(typeChecker, log, compilerOptions, sourceFile, position, entryId, allSourceFiles);
|
||||
switch (symbolCompletion.type) {
|
||||
@@ -550,29 +566,40 @@ namespace ts.Completions {
|
||||
case "symbol": {
|
||||
const { symbol, location, symbolToOriginInfoMap, previousToken } = symbolCompletion;
|
||||
const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles, preferences);
|
||||
const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol);
|
||||
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
|
||||
return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions, source: sourceDisplay };
|
||||
return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, codeActions, sourceDisplay);
|
||||
}
|
||||
case "none": {
|
||||
case "none":
|
||||
// Didn't find a symbol with this name. See if we can find a keyword instead.
|
||||
if (allKeywordsCompletions().some(c => c.name === name)) {
|
||||
return {
|
||||
name,
|
||||
kind: ScriptElementKind.keyword,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
displayParts: [displayPart(name, SymbolDisplayPartKind.keyword)],
|
||||
documentation: undefined,
|
||||
tags: undefined,
|
||||
codeActions: undefined,
|
||||
source: undefined,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return allKeywordsCompletions().some(c => c.name === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.keyword, [displayPart(name, SymbolDisplayPartKind.keyword)]) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
|
||||
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All);
|
||||
return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
|
||||
}
|
||||
|
||||
function stringLiteralCompletionDetails(name: string, location: Node, completion: StringLiteralCompletion, sourceFile: SourceFile, checker: TypeChecker): CompletionEntryDetails | undefined {
|
||||
switch (completion.kind) {
|
||||
case StringLiteralCompletionKind.Paths: {
|
||||
const match = find(completion.paths, p => p.name === name);
|
||||
return match && createCompletionDetails(name, ScriptElementKindModifier.none, match.kind, [textPart(name)]);
|
||||
}
|
||||
case StringLiteralCompletionKind.Properties: {
|
||||
const match = find(completion.symbols, s => s.name === name);
|
||||
return match && createCompletionDetailsForSymbol(match, checker, sourceFile, location);
|
||||
}
|
||||
case StringLiteralCompletionKind.Types:
|
||||
return find(completion.types, t => t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.typeElement, [textPart(name)]) : undefined;
|
||||
default:
|
||||
return Debug.assertNever(completion);
|
||||
}
|
||||
}
|
||||
|
||||
function createCompletionDetails(name: string, kindModifiers: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], documentation?: SymbolDisplayPart[], tags?: JSDocTagInfo[], codeActions?: CodeAction[], source?: SymbolDisplayPart[]): CompletionEntryDetails {
|
||||
return { name, kindModifiers, kind, displayParts, documentation, tags, codeActions, source };
|
||||
}
|
||||
|
||||
interface CodeActionsAndSourceDisplay {
|
||||
readonly codeActions: CodeAction[] | undefined;
|
||||
readonly sourceDisplay: SymbolDisplayPart[] | undefined;
|
||||
@@ -1031,6 +1058,8 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function addTypeProperties(type: Type): void {
|
||||
isNewIdentifierLocation = hasIndexSignature(type);
|
||||
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
// In javascript files, for union types, we don't just get the members that
|
||||
// the individual types have in common, we also include all the members that
|
||||
@@ -1380,10 +1409,10 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
// Previous token may have been a keyword that was converted to an identifier.
|
||||
switch (previousToken.getText()) {
|
||||
case "public":
|
||||
case "protected":
|
||||
case "private":
|
||||
switch (keywordForNode(previousToken)) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1432,11 +1461,9 @@ namespace ts.Completions {
|
||||
let existingMembers: ReadonlyArray<Declaration>;
|
||||
|
||||
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
// We are completing on contextual types, but may also include properties
|
||||
// other than those within the declared type.
|
||||
isNewIdentifierLocation = true;
|
||||
const typeForObject = typeChecker.getContextualType(objectLikeContainer);
|
||||
if (!typeForObject) return GlobalsSearch.Fail;
|
||||
isNewIdentifierLocation = hasIndexSignature(typeForObject);
|
||||
typeMembers = getPropertiesForCompletion(typeForObject, typeChecker, /*isForAccess*/ false);
|
||||
existingMembers = objectLikeContainer.properties;
|
||||
}
|
||||
@@ -1535,43 +1562,34 @@ namespace ts.Completions {
|
||||
completionKind = CompletionKind.MemberLike;
|
||||
// Declaring new property/method/accessor
|
||||
isNewIdentifierLocation = true;
|
||||
// Has keywords for class elements
|
||||
keywordFilters = isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
|
||||
|
||||
// If you're in an interface you don't want to repeat things from super-interface. So just stop here.
|
||||
if (!isClassLike(decl)) return GlobalsSearch.Success;
|
||||
|
||||
const baseTypeNode = getClassExtendsHeritageClauseElement(decl);
|
||||
const implementsTypeNodes = getClassImplementsHeritageClauseElements(decl);
|
||||
if (!baseTypeNode && !implementsTypeNodes) return GlobalsSearch.Success;
|
||||
|
||||
const classElement = contextToken.parent;
|
||||
const classElementModifierFlags = (isClassElement(classElement) ? getModifierFlags(classElement) : ModifierFlags.None)
|
||||
// If this is context token is not something we are editing now, consider if this would lead to be modifier
|
||||
| (isIdentifier(contextToken) && !isCurrentlyEditingNode(contextToken) ? modifierToFlag(contextToken.originalKeywordKind) : ModifierFlags.None);
|
||||
|
||||
// No member list for private methods
|
||||
if (classElementModifierFlags & ModifierFlags.Private) return GlobalsSearch.Success;
|
||||
|
||||
let baseClassTypeToGetPropertiesFrom: Type | undefined;
|
||||
if (baseTypeNode) {
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
|
||||
if (classElementModifierFlags & ModifierFlags.Static) {
|
||||
// Use static class to get property symbols from
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(baseClassTypeToGetPropertiesFrom.symbol, decl);
|
||||
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
|
||||
// If this is context token is not something we are editing now, consider if this would lead to be modifier
|
||||
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
|
||||
switch (contextToken.getText()) {
|
||||
case "private":
|
||||
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
|
||||
break;
|
||||
case "static":
|
||||
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const implementedInterfaceTypePropertySymbols = !implementsTypeNodes || (classElementModifierFlags & ModifierFlags.Static)
|
||||
? emptyArray
|
||||
: flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
|
||||
|
||||
// List of property symbols of base type that are not private and already implemented
|
||||
symbols = filterClassMembersList(
|
||||
baseClassTypeToGetPropertiesFrom ? typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) : emptyArray,
|
||||
implementedInterfaceTypePropertySymbols,
|
||||
decl.members,
|
||||
classElementModifierFlags);
|
||||
// No member list for private methods
|
||||
if (!(classElementModifierFlags & ModifierFlags.Private)) {
|
||||
// List of property symbols of base type that are not private and already implemented
|
||||
const baseSymbols = flatMap(getAllSuperTypeNodes(decl), baseTypeNode => {
|
||||
const type = typeChecker.getTypeAtLocation(baseTypeNode);
|
||||
return typeChecker.getPropertiesOfType(classElementModifierFlags & ModifierFlags.Static ? typeChecker.getTypeOfSymbolAtLocation(type.symbol, decl) : type);
|
||||
});
|
||||
symbols = filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags);
|
||||
}
|
||||
|
||||
return GlobalsSearch.Success;
|
||||
}
|
||||
@@ -1616,14 +1634,9 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isParameterOfConstructorDeclaration(node: Node) {
|
||||
return isParameter(node) && isConstructorDeclaration(node.parent);
|
||||
}
|
||||
|
||||
function isConstructorParameterCompletion(node: Node) {
|
||||
return node.parent &&
|
||||
isParameterOfConstructorDeclaration(node.parent) &&
|
||||
(isConstructorParameterCompletionKeyword(node.kind) || isDeclarationName(node));
|
||||
function isConstructorParameterCompletion(node: Node): boolean {
|
||||
return !!node.parent && isParameter(node.parent) && isConstructorDeclaration(node.parent.parent)
|
||||
&& (isParameterPropertyModifier(node.kind) || isDeclarationName(node));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1808,8 +1821,7 @@ namespace ts.Completions {
|
||||
|
||||
// If the previous token is keyword correspoding to class member completion keyword
|
||||
// there will be completion available here
|
||||
if (isClassMemberCompletionKeywordText(contextToken.getText()) &&
|
||||
isFromObjectTypeDeclaration(contextToken)) {
|
||||
if (isClassMemberCompletionKeyword(keywordForNode(contextToken)) && isFromObjectTypeDeclaration(contextToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1819,29 +1831,29 @@ namespace ts.Completions {
|
||||
// - its name of the parameter and not being edited
|
||||
// eg. constructor(a |<- this shouldnt show completion
|
||||
if (!isIdentifier(contextToken) ||
|
||||
isConstructorParameterCompletionKeywordText(contextToken.getText()) ||
|
||||
isParameterPropertyModifier(keywordForNode(contextToken)) ||
|
||||
isCurrentlyEditingNode(contextToken)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Previous token may have been a keyword that was converted to an identifier.
|
||||
switch (contextToken.getText()) {
|
||||
case "abstract":
|
||||
case "async":
|
||||
case "class":
|
||||
case "const":
|
||||
case "declare":
|
||||
case "enum":
|
||||
case "function":
|
||||
case "interface":
|
||||
case "let":
|
||||
case "private":
|
||||
case "protected":
|
||||
case "public":
|
||||
case "static":
|
||||
case "var":
|
||||
case "yield":
|
||||
switch (keywordForNode(contextToken)) {
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
case SyntaxKind.AsyncKeyword:
|
||||
case SyntaxKind.ClassKeyword:
|
||||
case SyntaxKind.ConstKeyword:
|
||||
case SyntaxKind.DeclareKeyword:
|
||||
case SyntaxKind.EnumKeyword:
|
||||
case SyntaxKind.FunctionKeyword:
|
||||
case SyntaxKind.InterfaceKeyword:
|
||||
case SyntaxKind.LetKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.VarKeyword:
|
||||
case SyntaxKind.YieldKeyword:
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1945,12 +1957,8 @@ namespace ts.Completions {
|
||||
*
|
||||
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
|
||||
*/
|
||||
function filterClassMembersList(
|
||||
baseSymbols: ReadonlyArray<Symbol>,
|
||||
implementingTypeSymbols: ReadonlyArray<Symbol>,
|
||||
existingMembers: ReadonlyArray<ClassElement>,
|
||||
currentClassElementModifierFlags: ModifierFlags): Symbol[] {
|
||||
const existingMemberNames = createUnderscoreEscapedMap<boolean>();
|
||||
function filterClassMembersList(baseSymbols: ReadonlyArray<Symbol>, existingMembers: ReadonlyArray<ClassElement>, currentClassElementModifierFlags: ModifierFlags): Symbol[] {
|
||||
const existingMemberNames = createUnderscoreEscapedMap<true>();
|
||||
for (const m of existingMembers) {
|
||||
// Ignore omitted expressions for missing members
|
||||
if (m.kind !== SyntaxKind.PropertyDeclaration &&
|
||||
@@ -1971,10 +1979,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
// do not filter it out if the static presence doesnt match
|
||||
const mIsStatic = hasModifier(m, ModifierFlags.Static);
|
||||
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
|
||||
if ((mIsStatic && !currentElementIsStatic) ||
|
||||
(!mIsStatic && currentElementIsStatic)) {
|
||||
if (hasModifier(m, ModifierFlags.Static) !== !!(currentClassElementModifierFlags & ModifierFlags.Static)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1984,24 +1989,10 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
const result: Symbol[] = [];
|
||||
addPropertySymbols(baseSymbols, ModifierFlags.Private);
|
||||
addPropertySymbols(implementingTypeSymbols, ModifierFlags.NonPublicAccessibilityModifier);
|
||||
return result;
|
||||
|
||||
function addPropertySymbols(properties: ReadonlyArray<Symbol>, inValidModifierFlags: ModifierFlags) {
|
||||
for (const property of properties) {
|
||||
if (isValidProperty(property, inValidModifierFlags)) {
|
||||
result.push(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
|
||||
return !existingMemberNames.get(propertySymbol.escapedName) &&
|
||||
propertySymbol.getDeclarations() &&
|
||||
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
|
||||
}
|
||||
return baseSymbols.filter(propertySymbol =>
|
||||
!existingMemberNames.has(propertySymbol.escapedName) &&
|
||||
!!propertySymbol.declarations &&
|
||||
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2027,7 +2018,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function isCurrentlyEditingNode(node: Node): boolean {
|
||||
return node.getStart() <= position && position <= node.getEnd();
|
||||
return node.getStart(sourceFile) <= position && position <= node.getEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2097,9 +2088,9 @@ namespace ts.Completions {
|
||||
case KeywordCompletionFilters.InterfaceElementKeywords:
|
||||
return isInterfaceOrTypeLiteralCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.ConstructorParameterKeywords:
|
||||
return isConstructorParameterCompletionKeyword(kind);
|
||||
return isParameterPropertyModifier(kind);
|
||||
case KeywordCompletionFilters.FunctionLikeBodyKeywords:
|
||||
return isFunctionLikeBodyCompletionKeyword(kind);
|
||||
return !isClassMemberCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.TypeKeywords:
|
||||
return isTypeKeyword(kind);
|
||||
default:
|
||||
@@ -2114,53 +2105,19 @@ namespace ts.Completions {
|
||||
|
||||
function isClassMemberCompletionKeyword(kind: SyntaxKind) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ConstructorKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
case SyntaxKind.AsyncKeyword:
|
||||
return true;
|
||||
default:
|
||||
return isClassMemberModifier(kind);
|
||||
}
|
||||
}
|
||||
|
||||
function isClassMemberCompletionKeywordText(text: string) {
|
||||
return isClassMemberCompletionKeyword(stringToToken(text));
|
||||
}
|
||||
|
||||
function isConstructorParameterCompletionKeyword(kind: SyntaxKind) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function isConstructorParameterCompletionKeywordText(text: string) {
|
||||
return isConstructorParameterCompletionKeyword(stringToToken(text));
|
||||
}
|
||||
|
||||
function isFunctionLikeBodyCompletionKeyword(kind: SyntaxKind) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
case SyntaxKind.ConstructorKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
function keywordForNode(node: Node): SyntaxKind {
|
||||
return isIdentifier(node) ? node.originalKeywordKind || SyntaxKind.Unknown : node.kind;
|
||||
}
|
||||
|
||||
function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator {
|
||||
@@ -2260,4 +2217,8 @@ namespace ts.Completions {
|
||||
function isFromObjectTypeDeclaration(node: Node): boolean {
|
||||
return node.parent && (isClassElement(node.parent) || isTypeElement(node.parent)) && isObjectTypeDeclaration(node.parent.parent);
|
||||
}
|
||||
|
||||
function hasIndexSignature(type: Type): boolean {
|
||||
return !!type.getStringIndexType() || !!type.getNumberIndexType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,15 +132,8 @@ namespace ts.FindAllReferences {
|
||||
|
||||
const { node, name, kind, displayParts } = info;
|
||||
const sourceFile = node.getSourceFile();
|
||||
return {
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: sourceFile.fileName,
|
||||
kind,
|
||||
name,
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
displayParts
|
||||
};
|
||||
const textSpan = getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile);
|
||||
return { containerKind: ScriptElementKind.unknown, containerName: "", fileName: sourceFile.fileName, kind, name, textSpan, displayParts };
|
||||
}
|
||||
|
||||
function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
|
||||
@@ -218,8 +211,8 @@ namespace ts.FindAllReferences {
|
||||
return { fileName, span };
|
||||
}
|
||||
|
||||
function getTextSpan(node: Node): TextSpan {
|
||||
let start = node.getStart();
|
||||
function getTextSpan(node: Node, sourceFile?: SourceFile): TextSpan {
|
||||
let start = node.getStart(sourceFile);
|
||||
let end = node.getEnd();
|
||||
if (node.kind === SyntaxKind.StringLiteral) {
|
||||
start += 1;
|
||||
@@ -366,7 +359,7 @@ namespace ts.FindAllReferences.Core {
|
||||
// otherwise we'll need to search globally (i.e. include each file).
|
||||
const scope = getSymbolScope(symbol);
|
||||
if (scope) {
|
||||
getReferencesInContainer(scope, scope.getSourceFile(), search, state);
|
||||
getReferencesInContainer(scope, scope.getSourceFile(), search, state, /*addReferencesHere*/ !(isSourceFile(scope) && !contains(sourceFiles, scope)));
|
||||
}
|
||||
else {
|
||||
// Global search
|
||||
@@ -595,9 +588,8 @@ namespace ts.FindAllReferences.Core {
|
||||
function searchForImportedSymbol(symbol: Symbol, state: State): void {
|
||||
for (const declaration of symbol.declarations) {
|
||||
const exportingFile = declaration.getSourceFile();
|
||||
if (state.includesSourceFile(exportingFile)) {
|
||||
getReferencesInSourceFile(exportingFile, state.createSearch(declaration, symbol, ImportExport.Import), state);
|
||||
}
|
||||
// Need to search in the file even if it's not in the search-file set, because it might export the symbol.
|
||||
getReferencesInSourceFile(exportingFile, state.createSearch(declaration, symbol, ImportExport.Import), state, state.includesSourceFile(exportingFile));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,9 +792,9 @@ namespace ts.FindAllReferences.Core {
|
||||
return references.length ? [{ definition: { type: "keyword", node: references[0].node }, references }] : undefined;
|
||||
}
|
||||
|
||||
function getReferencesInSourceFile(sourceFile: SourceFile, search: Search, state: State): void {
|
||||
function getReferencesInSourceFile(sourceFile: SourceFile, search: Search, state: State, addReferencesHere = true): void {
|
||||
state.cancellationToken.throwIfCancellationRequested();
|
||||
return getReferencesInContainer(sourceFile, sourceFile, search, state);
|
||||
return getReferencesInContainer(sourceFile, sourceFile, search, state, addReferencesHere);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -810,17 +802,17 @@ namespace ts.FindAllReferences.Core {
|
||||
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
|
||||
* searchLocation: a node where the search value
|
||||
*/
|
||||
function getReferencesInContainer(container: Node, sourceFile: SourceFile, search: Search, state: State): void {
|
||||
function getReferencesInContainer(container: Node, sourceFile: SourceFile, search: Search, state: State, addReferencesHere: boolean): void {
|
||||
if (!state.markSearchedSymbol(sourceFile, search.symbol)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container)) {
|
||||
getReferencesAtLocation(sourceFile, position, search, state);
|
||||
getReferencesAtLocation(sourceFile, position, search, state, addReferencesHere);
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesAtLocation(sourceFile: SourceFile, position: number, search: Search, state: State): void {
|
||||
function getReferencesAtLocation(sourceFile: SourceFile, position: number, search: Search, state: State, addReferencesHere: boolean): void {
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
|
||||
if (!isValidReferencePosition(referenceLocation, search.text)) {
|
||||
@@ -855,7 +847,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
if (isExportSpecifier(parent)) {
|
||||
Debug.assert(referenceLocation.kind === SyntaxKind.Identifier);
|
||||
getReferencesAtExportSpecifier(referenceLocation as Identifier, referenceSymbol, parent, search, state);
|
||||
getReferencesAtExportSpecifier(referenceLocation as Identifier, referenceSymbol, parent, search, state, addReferencesHere);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -867,7 +859,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
switch (state.specialSearchKind) {
|
||||
case SpecialSearchKind.None:
|
||||
addReference(referenceLocation, relatedSymbol, state);
|
||||
if (addReferencesHere) addReference(referenceLocation, relatedSymbol, state);
|
||||
break;
|
||||
case SpecialSearchKind.Constructor:
|
||||
addConstructorReferences(referenceLocation, sourceFile, search, state);
|
||||
@@ -882,7 +874,7 @@ namespace ts.FindAllReferences.Core {
|
||||
getImportOrExportReferences(referenceLocation, referenceSymbol, search, state);
|
||||
}
|
||||
|
||||
function getReferencesAtExportSpecifier(referenceLocation: Identifier, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, search: Search, state: State): void {
|
||||
function getReferencesAtExportSpecifier(referenceLocation: Identifier, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, search: Search, state: State, addReferencesHere: boolean): void {
|
||||
const { parent, propertyName, name } = exportSpecifier;
|
||||
const exportDeclaration = parent.parent;
|
||||
const localSymbol = getLocalSymbolForExportSpecifier(referenceLocation, referenceSymbol, exportSpecifier, state.checker);
|
||||
@@ -900,7 +892,7 @@ namespace ts.FindAllReferences.Core {
|
||||
addRef();
|
||||
}
|
||||
|
||||
if (!state.options.isForRename && state.markSeenReExportRHS(name)) {
|
||||
if (addReferencesHere && !state.options.isForRename && state.markSeenReExportRHS(name)) {
|
||||
addReference(name, referenceSymbol, state);
|
||||
}
|
||||
}
|
||||
@@ -925,7 +917,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function addRef() {
|
||||
addReference(referenceLocation, localSymbol, state);
|
||||
if (addReferencesHere) addReference(referenceLocation, localSymbol, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1208,63 +1200,31 @@ namespace ts.FindAllReferences.Core {
|
||||
* distinction between structurally compatible implementations and explicit implementations, so we
|
||||
* must use the AST.
|
||||
*
|
||||
* @param child A class or interface Symbol
|
||||
* @param symbol A class or interface Symbol
|
||||
* @param parent Another class or interface Symbol
|
||||
* @param cachedResults A map of symbol id pairs (i.e. "child,parent") to booleans indicating previous results
|
||||
*/
|
||||
function explicitlyInheritsFrom(child: Symbol, parent: Symbol, cachedResults: Map<boolean>, checker: TypeChecker): boolean {
|
||||
const parentIsInterface = parent.getFlags() & SymbolFlags.Interface;
|
||||
return searchHierarchy(child);
|
||||
|
||||
function searchHierarchy(symbol: Symbol): boolean {
|
||||
if (symbol === parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const key = getSymbolId(symbol) + "," + getSymbolId(parent);
|
||||
const cached = cachedResults.get(key);
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Set the key so that we don't infinitely recurse
|
||||
cachedResults.set(key, false);
|
||||
|
||||
const inherits = forEach(symbol.getDeclarations(), declaration => {
|
||||
if (isClassLike(declaration)) {
|
||||
if (parentIsInterface) {
|
||||
const interfaceReferences = getClassImplementsHeritageClauseElements(declaration);
|
||||
if (interfaceReferences) {
|
||||
for (const typeReference of interfaceReferences) {
|
||||
if (searchTypeReference(typeReference)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return searchTypeReference(getClassExtendsHeritageClauseElement(declaration));
|
||||
}
|
||||
else if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
|
||||
if (parentIsInterface) {
|
||||
return forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), searchTypeReference);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
cachedResults.set(key, inherits);
|
||||
return inherits;
|
||||
function explicitlyInheritsFrom(symbol: Symbol, parent: Symbol, cachedResults: Map<boolean>, checker: TypeChecker): boolean {
|
||||
if (symbol === parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function searchTypeReference(typeReference: ExpressionWithTypeArguments): boolean {
|
||||
if (typeReference) {
|
||||
const key = getSymbolId(symbol) + "," + getSymbolId(parent);
|
||||
const cached = cachedResults.get(key);
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Set the key so that we don't infinitely recurse
|
||||
cachedResults.set(key, false);
|
||||
|
||||
const inherits = symbol.declarations.some(declaration =>
|
||||
getAllSuperTypeNodes(declaration).some(typeReference => {
|
||||
const type = checker.getTypeAtLocation(typeReference);
|
||||
if (type && type.symbol) {
|
||||
return searchHierarchy(type.symbol);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return !!type && !!type.symbol && explicitlyInheritsFrom(type.symbol, parent, cachedResults, checker);
|
||||
}));
|
||||
cachedResults.set(key, inherits);
|
||||
return inherits;
|
||||
}
|
||||
|
||||
function getReferencesForSuperKeyword(superKeyword: Node): SymbolAndEntries[] {
|
||||
@@ -1499,10 +1459,6 @@ namespace ts.FindAllReferences.Core {
|
||||
* The value of previousIterationSymbol is undefined when the function is first called.
|
||||
*/
|
||||
function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Push<Symbol>, previousIterationSymbolsCache: SymbolTable, checker: TypeChecker): void {
|
||||
if (!symbol) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the current symbol is the same as the previous-iteration symbol, we can just return the symbol that has already been visited
|
||||
// This is particularly important for the following cases, so that we do not infinitely visit the same symbol.
|
||||
// For example:
|
||||
@@ -1514,27 +1470,16 @@ namespace ts.FindAllReferences.Core {
|
||||
// the function will add any found symbol of the property-name, then its sub-routine will call
|
||||
// getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already
|
||||
// visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol.
|
||||
if (previousIterationSymbolsCache.has(symbol.escapedName)) {
|
||||
if (!symbol || previousIterationSymbolsCache.has(symbol.escapedName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
forEach(symbol.getDeclarations(), declaration => {
|
||||
if (isClassLike(declaration)) {
|
||||
getPropertySymbolFromTypeReference(getClassExtendsHeritageClauseElement(<ClassDeclaration>declaration));
|
||||
forEach(getClassImplementsHeritageClauseElements(<ClassDeclaration>declaration), getPropertySymbolFromTypeReference);
|
||||
}
|
||||
else if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
|
||||
forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), getPropertySymbolFromTypeReference);
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
for (const declaration of symbol.declarations) {
|
||||
for (const typeReference of getAllSuperTypeNodes(declaration)) {
|
||||
const type = checker.getTypeAtLocation(typeReference);
|
||||
if (!type) continue;
|
||||
|
||||
function getPropertySymbolFromTypeReference(typeReference: ExpressionWithTypeArguments): void {
|
||||
if (typeReference) {
|
||||
const type = checker.getTypeAtLocation(typeReference);
|
||||
if (type) {
|
||||
const propertySymbol = checker.getPropertyOfType(type, propertyName);
|
||||
if (propertySymbol) {
|
||||
result.push(...checker.getRootSymbols(propertySymbol));
|
||||
|
||||
@@ -394,18 +394,16 @@ namespace ts.FindAllReferences {
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
case SyntaxKind.ImportDeclaration: {
|
||||
const decl = statement as ImportDeclaration | ExportDeclaration;
|
||||
if (decl.moduleSpecifier && decl.moduleSpecifier.kind === SyntaxKind.StringLiteral) {
|
||||
action(decl, decl.moduleSpecifier as StringLiteral);
|
||||
if (decl.moduleSpecifier && isStringLiteral(decl.moduleSpecifier)) {
|
||||
action(decl, decl.moduleSpecifier);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.ImportEqualsDeclaration: {
|
||||
const decl = statement as ImportEqualsDeclaration;
|
||||
const { moduleReference } = decl;
|
||||
if (moduleReference.kind === SyntaxKind.ExternalModuleReference &&
|
||||
moduleReference.expression.kind === SyntaxKind.StringLiteral) {
|
||||
action(decl, moduleReference.expression as StringLiteral);
|
||||
if (isExternalModuleImportEquals(decl)) {
|
||||
action(decl, decl.moduleReference.expression);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -647,7 +645,7 @@ namespace ts.FindAllReferences {
|
||||
return node.kind === SyntaxKind.ModuleDeclaration && (node as ModuleDeclaration).name.kind === SyntaxKind.StringLiteral;
|
||||
}
|
||||
|
||||
function isExternalModuleImportEquals({ moduleReference }: ImportEqualsDeclaration): boolean {
|
||||
return moduleReference.kind === SyntaxKind.ExternalModuleReference && moduleReference.expression.kind === SyntaxKind.StringLiteral;
|
||||
function isExternalModuleImportEquals(eq: ImportEqualsDeclaration): eq is ImportEqualsDeclaration & { moduleReference: { expression: StringLiteral } } {
|
||||
return eq.moduleReference.kind === SyntaxKind.ExternalModuleReference && eq.moduleReference.expression.kind === SyntaxKind.StringLiteral;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,33 +5,40 @@ namespace ts.Completions.PathCompletions {
|
||||
readonly kind: ScriptElementKind.scriptElement | ScriptElementKind.directory | ScriptElementKind.externalModuleName;
|
||||
}
|
||||
export interface PathCompletion extends NameAndKind {
|
||||
readonly span: TextSpan;
|
||||
}
|
||||
function createPathCompletion(name: string, kind: PathCompletion["kind"], span: TextSpan): PathCompletion {
|
||||
return { name, kind, span };
|
||||
readonly span: TextSpan | undefined;
|
||||
}
|
||||
|
||||
export function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): PathCompletion[] {
|
||||
function nameAndKind(name: string, kind: NameAndKind["kind"]): NameAndKind {
|
||||
return { name, kind };
|
||||
}
|
||||
function addReplacementSpans(text: string, textStart: number, names: ReadonlyArray<NameAndKind>): ReadonlyArray<PathCompletion> {
|
||||
const span = getDirectoryFragmentTextSpan(text, textStart);
|
||||
return names.map(({ name, kind }): PathCompletion => ({ name, kind, span }));
|
||||
}
|
||||
|
||||
export function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): ReadonlyArray<PathCompletion> {
|
||||
return addReplacementSpans(node.text, node.getStart(sourceFile) + 1, getStringLiteralCompletionsFromModuleNamesWorker(node, compilerOptions, host, typeChecker));
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionsFromModuleNamesWorker(node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): ReadonlyArray<NameAndKind> {
|
||||
const literalValue = normalizeSlashes(node.text);
|
||||
|
||||
const scriptPath = node.getSourceFile().path;
|
||||
const scriptDirectory = getDirectoryPath(scriptPath);
|
||||
|
||||
const span = getDirectoryFragmentTextSpan(node.text, node.getStart(sourceFile) + 1);
|
||||
if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) {
|
||||
const extensions = getSupportedExtensions(compilerOptions);
|
||||
if (compilerOptions.rootDirs) {
|
||||
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
|
||||
compilerOptions.rootDirs, literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, span, compilerOptions, host, scriptPath);
|
||||
compilerOptions.rootDirs, literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, compilerOptions, host, scriptPath);
|
||||
}
|
||||
else {
|
||||
return getCompletionEntriesForDirectoryFragment(
|
||||
literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, span, host, scriptPath);
|
||||
return getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, host, scriptPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check for node modules
|
||||
return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span, compilerOptions, host, typeChecker);
|
||||
return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, compilerOptions, host, typeChecker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,15 +61,15 @@ namespace ts.Completions.PathCompletions {
|
||||
compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): PathCompletion[] {
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): NameAndKind[] {
|
||||
const basePath = compilerOptions.project || host.getCurrentDirectory();
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
|
||||
|
||||
const result: PathCompletion[] = [];
|
||||
const result: NameAndKind[] = [];
|
||||
|
||||
for (const baseDirectory of baseDirectories) {
|
||||
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, host, exclude, result);
|
||||
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, host, exclude, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -71,7 +78,7 @@ namespace ts.Completions.PathCompletions {
|
||||
/**
|
||||
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
|
||||
*/
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: PathCompletion[] = []): PathCompletion[] {
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, host: LanguageServiceHost, exclude?: string, result: NameAndKind[] = []): NameAndKind[] {
|
||||
if (fragment === undefined) {
|
||||
fragment = "";
|
||||
}
|
||||
@@ -120,7 +127,7 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
|
||||
forEachKey(foundFiles, foundFile => {
|
||||
result.push(createPathCompletion(foundFile, ScriptElementKind.scriptElement, span));
|
||||
result.push(nameAndKind(foundFile, ScriptElementKind.scriptElement));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,7 +138,7 @@ namespace ts.Completions.PathCompletions {
|
||||
for (const directory of directories) {
|
||||
const directoryName = getBaseFileName(normalizePath(directory));
|
||||
|
||||
result.push(createPathCompletion(directoryName, ScriptElementKind.directory, span));
|
||||
result.push(nameAndKind(directoryName, ScriptElementKind.directory));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,16 +153,16 @@ namespace ts.Completions.PathCompletions {
|
||||
* Modules from node_modules (i.e. those listed in package.json)
|
||||
* This includes all files that are found in node_modules/moduleName/ with acceptable file extensions
|
||||
*/
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): PathCompletion[] {
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): NameAndKind[] {
|
||||
const { baseUrl, paths } = compilerOptions;
|
||||
|
||||
const result: PathCompletion[] = [];
|
||||
const result: NameAndKind[] = [];
|
||||
|
||||
const fileExtensions = getSupportedExtensions(compilerOptions);
|
||||
if (baseUrl) {
|
||||
const projectDir = compilerOptions.project || host.getCurrentDirectory();
|
||||
const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl);
|
||||
getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/ false, span, host, /*exclude*/ undefined, result);
|
||||
getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/ false, host, /*exclude*/ undefined, result);
|
||||
|
||||
for (const path in paths) {
|
||||
const patterns = paths[path];
|
||||
@@ -163,7 +170,7 @@ namespace ts.Completions.PathCompletions {
|
||||
for (const { name, kind } of getCompletionsForPathMapping(path, patterns, fragment, baseUrl, fileExtensions, host)) {
|
||||
// Path mappings may provide a duplicate way to get to something we've already added, so don't add again.
|
||||
if (!result.some(entry => entry.name === name)) {
|
||||
result.push(createPathCompletion(name, kind, span));
|
||||
result.push(nameAndKind(name, kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,15 +181,15 @@ namespace ts.Completions.PathCompletions {
|
||||
forEachAncestorDirectory(scriptPath, ancestor => {
|
||||
const nodeModules = combinePaths(ancestor, "node_modules");
|
||||
if (host.directoryExists(nodeModules)) {
|
||||
getCompletionEntriesForDirectoryFragment(fragment, nodeModules, fileExtensions, /*includeExtensions*/ false, span, host, /*exclude*/ undefined, result);
|
||||
getCompletionEntriesForDirectoryFragment(fragment, nodeModules, fileExtensions, /*includeExtensions*/ false, host, /*exclude*/ undefined, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
|
||||
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, result);
|
||||
|
||||
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, compilerOptions, typeChecker, host)) {
|
||||
result.push(createPathCompletion(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
result.push(nameAndKind(moduleName, ScriptElementKind.externalModuleName));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -296,7 +303,7 @@ namespace ts.Completions.PathCompletions {
|
||||
return deduplicate(nonRelativeModuleNames, equateStringsCaseSensitive, compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): PathCompletion[] | undefined {
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): ReadonlyArray<PathCompletion> | undefined {
|
||||
const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false);
|
||||
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end);
|
||||
@@ -311,23 +318,13 @@ namespace ts.Completions.PathCompletions {
|
||||
|
||||
const [, prefix, kind, toComplete] = match;
|
||||
const scriptPath = getDirectoryPath(sourceFile.path);
|
||||
switch (kind) {
|
||||
case "path": {
|
||||
// Give completions for a relative path
|
||||
const span = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length);
|
||||
return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(compilerOptions), /*includeExtensions*/ true, span, host, sourceFile.path);
|
||||
}
|
||||
case "types": {
|
||||
// Give completions based on the typings available
|
||||
const span = createTextSpan(range.pos + prefix.length, match[0].length - prefix.length);
|
||||
return getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span);
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(compilerOptions), /*includeExtensions*/ true, host, sourceFile.path)
|
||||
: kind === "types" ? getCompletionEntriesFromTypings(host, compilerOptions, scriptPath)
|
||||
: undefined;
|
||||
return names && addReplacementSpans(toComplete, range.pos + prefix.length, names);
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: PathCompletion[] = []): PathCompletion[] {
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: NameAndKind[] = []): NameAndKind[] {
|
||||
// Check for typings specified in compiler options
|
||||
const seen = createMap<true>();
|
||||
if (options.types) {
|
||||
@@ -375,7 +372,7 @@ namespace ts.Completions.PathCompletions {
|
||||
|
||||
function pushResult(moduleName: string) {
|
||||
if (!seen.has(moduleName)) {
|
||||
result.push(createPathCompletion(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
result.push(nameAndKind(moduleName, ScriptElementKind.externalModuleName));
|
||||
seen.set(moduleName, true);
|
||||
}
|
||||
}
|
||||
@@ -445,10 +442,12 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
|
||||
// Replace everything after the last directory seperator that appears
|
||||
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan {
|
||||
const index = text.lastIndexOf(directorySeparator);
|
||||
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan | undefined {
|
||||
const index = Math.max(text.lastIndexOf(directorySeparator), text.lastIndexOf("\\"));
|
||||
const offset = index !== -1 ? index + 1 : 0;
|
||||
return { start: textStart + offset, length: text.length - offset };
|
||||
// If the range is an identifier, span is unnecessary.
|
||||
const length = text.length - offset;
|
||||
return length === 0 || isIdentifierText(text.substr(offset, length), ScriptTarget.ESNext) ? undefined : createTextSpan(textStart + offset, length);
|
||||
}
|
||||
|
||||
// Returns true if the path is explicitly relative to the script (i.e. relative to . or ..)
|
||||
|
||||
@@ -1053,7 +1053,7 @@ namespace ts.refactor.extractSymbol {
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, /*blankLineBetween*/ true);
|
||||
|
||||
// Consume
|
||||
changeTracker.replaceNode(context.file, node, localReference, textChanges.useNonAdjustedPositions);
|
||||
changeTracker.replaceNode(context.file, node, localReference);
|
||||
}
|
||||
else {
|
||||
const newVariableDeclaration = createVariableDeclaration(localNameText, variableType, initializer);
|
||||
@@ -1070,7 +1070,7 @@ namespace ts.refactor.extractSymbol {
|
||||
|
||||
// Consume
|
||||
const localReference = createIdentifier(localNameText);
|
||||
changeTracker.replaceNode(context.file, node, localReference, textChanges.useNonAdjustedPositions);
|
||||
changeTracker.replaceNode(context.file, node, localReference);
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ExpressionStatement && scope === findAncestor(node, isScope)) {
|
||||
// If the parent is an expression statement and the target scope is the immediately enclosing one,
|
||||
@@ -1078,7 +1078,7 @@ namespace ts.refactor.extractSymbol {
|
||||
const newVariableStatement = createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([newVariableDeclaration], NodeFlags.Const));
|
||||
changeTracker.replaceNode(context.file, node.parent, newVariableStatement, textChanges.useNonAdjustedPositions);
|
||||
changeTracker.replaceNode(context.file, node.parent, newVariableStatement);
|
||||
}
|
||||
else {
|
||||
const newVariableStatement = createVariableStatement(
|
||||
@@ -1101,7 +1101,7 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
else {
|
||||
const localReference = createIdentifier(localNameText);
|
||||
changeTracker.replaceNode(context.file, node, localReference, textChanges.useNonAdjustedPositions);
|
||||
changeTracker.replaceNode(context.file, node, localReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-109
@@ -54,7 +54,7 @@ namespace ts {
|
||||
public jsDoc: JSDoc[];
|
||||
public original: Node;
|
||||
public transformFlags: TransformFlags;
|
||||
private _children: Node[];
|
||||
private _children: Node[] | undefined;
|
||||
|
||||
constructor(kind: SyntaxKind, pos: number, end: number) {
|
||||
this.pos = pos;
|
||||
@@ -117,106 +117,17 @@ namespace ts {
|
||||
return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd());
|
||||
}
|
||||
|
||||
private addSyntheticNodes(nodes: Push<Node>, pos: number, end: number): number {
|
||||
scanner.setTextPos(pos);
|
||||
while (pos < end) {
|
||||
const token = scanner.scan();
|
||||
const textPos = scanner.getTextPos();
|
||||
if (textPos <= end) {
|
||||
if (token === SyntaxKind.Identifier) {
|
||||
Debug.fail(`Did not expect ${Debug.showSyntaxKind(this)} to have an Identifier in its trivia`);
|
||||
}
|
||||
nodes.push(createNode(token, pos, textPos, this));
|
||||
}
|
||||
pos = textPos;
|
||||
if (token === SyntaxKind.EndOfFileToken) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private createSyntaxList(nodes: NodeArray<Node>): Node {
|
||||
const list = <NodeObject>createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, this);
|
||||
list._children = [];
|
||||
let pos = nodes.pos;
|
||||
|
||||
for (const node of nodes) {
|
||||
if (pos < node.pos) {
|
||||
pos = this.addSyntheticNodes(list._children, pos, node.pos);
|
||||
}
|
||||
list._children.push(node);
|
||||
pos = node.end;
|
||||
}
|
||||
if (pos < nodes.end) {
|
||||
this.addSyntheticNodes(list._children, pos, nodes.end);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private createChildren(sourceFile?: SourceFileLike) {
|
||||
if (!isNodeKind(this.kind)) {
|
||||
this._children = emptyArray;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJSDocCommentContainingNode(this)) {
|
||||
/** Don't add trivia for "tokens" since this is in a comment. */
|
||||
const children: Node[] = [];
|
||||
this.forEachChild(child => { children.push(child); });
|
||||
this._children = children;
|
||||
return;
|
||||
}
|
||||
|
||||
const children: Node[] = [];
|
||||
scanner.setText((sourceFile || this.getSourceFile()).text);
|
||||
let pos = this.pos;
|
||||
const processNode = (node: Node) => {
|
||||
pos = this.addSyntheticNodes(children, pos, node.pos);
|
||||
children.push(node);
|
||||
pos = node.end;
|
||||
};
|
||||
const processNodes = (nodes: NodeArray<Node>) => {
|
||||
if (pos < nodes.pos) {
|
||||
pos = this.addSyntheticNodes(children, pos, nodes.pos);
|
||||
}
|
||||
children.push(this.createSyntaxList(nodes));
|
||||
pos = nodes.end;
|
||||
};
|
||||
// jsDocComments need to be the first children
|
||||
if (this.jsDoc) {
|
||||
for (const jsDocComment of this.jsDoc) {
|
||||
processNode(jsDocComment);
|
||||
}
|
||||
}
|
||||
// For syntactic classifications, all trivia are classcified together, including jsdoc comments.
|
||||
// For that to work, the jsdoc comments should still be the leading trivia of the first child.
|
||||
// Restoring the scanner position ensures that.
|
||||
pos = this.pos;
|
||||
forEachChild(this, processNode, processNodes);
|
||||
if (pos < this.end) {
|
||||
this.addSyntheticNodes(children, pos, this.end);
|
||||
}
|
||||
scanner.setText(undefined);
|
||||
this._children = children;
|
||||
}
|
||||
|
||||
public getChildCount(sourceFile?: SourceFile): number {
|
||||
this.assertHasRealPosition();
|
||||
if (!this._children) this.createChildren(sourceFile);
|
||||
return this._children.length;
|
||||
return this.getChildren(sourceFile).length;
|
||||
}
|
||||
|
||||
public getChildAt(index: number, sourceFile?: SourceFile): Node {
|
||||
this.assertHasRealPosition();
|
||||
if (!this._children) this.createChildren(sourceFile);
|
||||
return this._children[index];
|
||||
return this.getChildren(sourceFile)[index];
|
||||
}
|
||||
|
||||
public getChildren(sourceFile?: SourceFileLike): Node[] {
|
||||
this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine");
|
||||
if (!this._children) this.createChildren(sourceFile);
|
||||
return this._children;
|
||||
return this._children || (this._children = createChildren(this, sourceFile));
|
||||
}
|
||||
|
||||
public getFirstToken(sourceFile?: SourceFile): Node {
|
||||
@@ -249,6 +160,74 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createChildren(node: Node, sourceFile: SourceFileLike | undefined): Node[] {
|
||||
if (!isNodeKind(node.kind)) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
const children: Node[] = [];
|
||||
|
||||
if (isJSDocCommentContainingNode(node)) {
|
||||
/** Don't add trivia for "tokens" since this is in a comment. */
|
||||
node.forEachChild(child => { children.push(child); });
|
||||
return children;
|
||||
}
|
||||
|
||||
scanner.setText((sourceFile || node.getSourceFile()).text);
|
||||
let pos = node.pos;
|
||||
const processNode = (child: Node) => {
|
||||
addSyntheticNodes(children, pos, child.pos, node);
|
||||
children.push(child);
|
||||
pos = child.end;
|
||||
};
|
||||
const processNodes = (nodes: NodeArray<Node>) => {
|
||||
addSyntheticNodes(children, pos, nodes.pos, node);
|
||||
children.push(createSyntaxList(nodes, node));
|
||||
pos = nodes.end;
|
||||
};
|
||||
// jsDocComments need to be the first children
|
||||
forEach((node as JSDocContainer).jsDoc, processNode);
|
||||
// For syntactic classifications, all trivia are classified together, including jsdoc comments.
|
||||
// For that to work, the jsdoc comments should still be the leading trivia of the first child.
|
||||
// Restoring the scanner position ensures that.
|
||||
pos = node.pos;
|
||||
node.forEachChild(processNode, processNodes);
|
||||
addSyntheticNodes(children, pos, node.end, node);
|
||||
scanner.setText(undefined);
|
||||
return children;
|
||||
}
|
||||
|
||||
function addSyntheticNodes(nodes: Push<Node>, pos: number, end: number, parent: Node): void {
|
||||
scanner.setTextPos(pos);
|
||||
while (pos < end) {
|
||||
const token = scanner.scan();
|
||||
const textPos = scanner.getTextPos();
|
||||
if (textPos <= end) {
|
||||
if (token === SyntaxKind.Identifier) {
|
||||
Debug.fail(`Did not expect ${Debug.showSyntaxKind(parent)} to have an Identifier in its trivia`);
|
||||
}
|
||||
nodes.push(createNode(token, pos, textPos, parent));
|
||||
}
|
||||
pos = textPos;
|
||||
if (token === SyntaxKind.EndOfFileToken) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createSyntaxList(nodes: NodeArray<Node>, parent: Node): Node {
|
||||
const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList;
|
||||
list._children = [];
|
||||
let pos = nodes.pos;
|
||||
for (const node of nodes) {
|
||||
addSyntheticNodes(list._children, pos, node.pos, parent);
|
||||
list._children.push(node);
|
||||
pos = node.end;
|
||||
}
|
||||
addSyntheticNodes(list._children, pos, nodes.end, parent);
|
||||
return list;
|
||||
}
|
||||
|
||||
class TokenOrIdentifierObject implements Node {
|
||||
public kind: SyntaxKind;
|
||||
public pos: number;
|
||||
@@ -576,7 +555,7 @@ namespace ts {
|
||||
*/
|
||||
function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] {
|
||||
let foundDocs = false;
|
||||
return flatMap(getAllSuperTypeNodes(declaration), superTypeNode => {
|
||||
return flatMap(declaration.parent ? getAllSuperTypeNodes(declaration.parent) : emptyArray, superTypeNode => {
|
||||
if (foundDocs) {
|
||||
return emptyArray;
|
||||
}
|
||||
@@ -594,21 +573,6 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the `TypeNode` for all super classes and implemented interfaces given a declaration.
|
||||
* @param declaration The possibly-inherited declaration.
|
||||
* @returns A filled array of `TypeNode`s containing all super classes and implemented interfaces if any exist, otherwise an empty array.
|
||||
*/
|
||||
function getAllSuperTypeNodes(declaration: Declaration): ReadonlyArray<TypeNode> {
|
||||
const container = declaration.parent;
|
||||
if (!container || (!isClassDeclaration(container) && !isInterfaceDeclaration(container))) {
|
||||
return emptyArray;
|
||||
}
|
||||
const extended = getClassExtendsHeritageClauseElement(container);
|
||||
const types = extended ? [extended] : emptyArray;
|
||||
return isClassLike(container) ? concatenate(types, getClassImplementsHeritageClauseElements(container)) : types;
|
||||
}
|
||||
|
||||
class SourceFileObject extends NodeObject implements SourceFile {
|
||||
public kind: SyntaxKind.SourceFile;
|
||||
public _declarationBrand: any;
|
||||
|
||||
+64
-76
@@ -103,10 +103,11 @@ namespace ts.textChanges {
|
||||
enum ChangeKind {
|
||||
Remove,
|
||||
ReplaceWithSingleNode,
|
||||
ReplaceWithMultipleNodes
|
||||
ReplaceWithMultipleNodes,
|
||||
Text,
|
||||
}
|
||||
|
||||
type Change = ReplaceWithSingleNode | ReplaceWithMultipleNodes | RemoveNode;
|
||||
type Change = ReplaceWithSingleNode | ReplaceWithMultipleNodes | RemoveNode | ChangeText;
|
||||
|
||||
interface BaseChange {
|
||||
readonly sourceFile: SourceFile;
|
||||
@@ -132,6 +133,15 @@ namespace ts.textChanges {
|
||||
readonly options?: InsertNodeOptions;
|
||||
}
|
||||
|
||||
interface ChangeText extends BaseChange {
|
||||
readonly kind: ChangeKind.Text;
|
||||
readonly text: string;
|
||||
}
|
||||
|
||||
function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd): TextRange {
|
||||
return { pos: getAdjustedStartPosition(sourceFile, startNode, options, Position.Start), end: getAdjustedEndPosition(sourceFile, endNode, options) };
|
||||
}
|
||||
|
||||
function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart, position: Position) {
|
||||
if (options.useNonAdjustedStartPosition) {
|
||||
return node.getStart(sourceFile);
|
||||
@@ -212,7 +222,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
/** Public for tests only. Other callers should use `ChangeTracker.with`. */
|
||||
constructor(public readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {}
|
||||
constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {}
|
||||
|
||||
public deleteRange(sourceFile: SourceFile, range: TextRange) {
|
||||
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range });
|
||||
@@ -280,46 +290,34 @@ namespace ts.textChanges {
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): default should probably be useNonAdjustedPositions
|
||||
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range, options, node: newNode });
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): default should probably be useNonAdjustedPositions
|
||||
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
|
||||
const pos = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
|
||||
const end = getAdjustedEndPosition(sourceFile, oldNode, options);
|
||||
return this.replaceRange(sourceFile, { pos, end }, newNode, options);
|
||||
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
return this.replaceRange(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNode, options);
|
||||
}
|
||||
|
||||
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): default should probably be useNonAdjustedPositions
|
||||
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
|
||||
const pos = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
|
||||
const end = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||
return this.replaceRange(sourceFile, { pos, end }, newNode, options);
|
||||
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
this.replaceRange(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNode, options);
|
||||
}
|
||||
|
||||
public replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: InsertNodeOptions = {}) {
|
||||
private replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: InsertNodeOptions = {}) {
|
||||
this.changes.push({ kind: ChangeKind.ReplaceWithMultipleNodes, sourceFile, range, options, nodes: newNodes });
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
const pos = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
|
||||
const end = getAdjustedEndPosition(sourceFile, oldNode, options);
|
||||
return this.replaceRangeWithNodes(sourceFile, { pos, end }, newNodes, options);
|
||||
return this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNodes, options);
|
||||
}
|
||||
|
||||
public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions) {
|
||||
const pos = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
|
||||
const end = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||
return this.replaceRangeWithNodes(sourceFile, { pos, end }, newNodes, options);
|
||||
return this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNodes, options);
|
||||
}
|
||||
|
||||
private insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, options, node: newNode, range: { pos, end: pos } });
|
||||
return this;
|
||||
this.replaceRange(sourceFile, createTextRange(pos), newNode, options);
|
||||
}
|
||||
|
||||
private insertNodesAt(sourceFile: SourceFile, pos: number, newNodes: ReadonlyArray<Node>, options: InsertNodeOptions = {}): void {
|
||||
@@ -344,6 +342,23 @@ namespace ts.textChanges {
|
||||
this.replaceRange(sourceFile, { pos, end: pos }, createToken(modifier), { suffix: " " });
|
||||
}
|
||||
|
||||
public insertCommentBeforeLine(sourceFile: SourceFile, lineNumber: number, position: number, commentText: string): void {
|
||||
const lineStartPosition = getStartPositionOfLine(lineNumber, sourceFile);
|
||||
const startPosition = getFirstNonSpaceCharacterPosition(sourceFile.text, lineStartPosition);
|
||||
// First try to see if we can put the comment on the previous line.
|
||||
// We need to make sure that we are not in the middle of a string literal or a comment.
|
||||
// If so, we do not want to separate the node from its comment if we can.
|
||||
// Otherwise, add an extra new line immediately before the error span.
|
||||
const insertAtLineStart = isValidLocationToAddComment(sourceFile, startPosition);
|
||||
const token = getTouchingToken(sourceFile, insertAtLineStart ? startPosition : position, /*includeJsDocComment*/ false);
|
||||
const text = `${insertAtLineStart ? "" : this.newLineCharacter}${sourceFile.text.slice(lineStartPosition, startPosition)}//${commentText}${this.newLineCharacter}`;
|
||||
this.insertText(sourceFile, token.getStart(sourceFile), text);
|
||||
}
|
||||
|
||||
private insertText(sourceFile: SourceFile, pos: number, text: string): void {
|
||||
this.changes.push({ kind: ChangeKind.Text, sourceFile, range: { pos, end: pos }, text });
|
||||
}
|
||||
|
||||
/** Prefer this over replacing a node with another that has a type annotation, as it avoids reformatting the other parts of the node. */
|
||||
public insertTypeAnnotation(sourceFile: SourceFile, node: TypeAnnotatable, type: TypeNode): void {
|
||||
const end = (isFunctionLike(node)
|
||||
@@ -359,7 +374,7 @@ namespace ts.textChanges {
|
||||
this.insertNodesAt(sourceFile, start, typeParameters, { prefix: "<", suffix: ">" });
|
||||
}
|
||||
|
||||
private getOptionsForInsertNodeBefore(before: Node, doubleNewlines: boolean): ChangeNodeOptions {
|
||||
private getOptionsForInsertNodeBefore(before: Node, doubleNewlines: boolean): InsertNodeOptions {
|
||||
if (isStatement(before) || isClassElement(before)) {
|
||||
return { suffix: doubleNewlines ? this.newLineCharacter + this.newLineCharacter : this.newLineCharacter };
|
||||
}
|
||||
@@ -393,7 +408,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
private replaceConstructorBody(sourceFile: SourceFile, ctr: ConstructorDeclaration, statements: ReadonlyArray<Statement>): void {
|
||||
this.replaceNode(sourceFile, ctr.body, createBlock(statements, /*multiLine*/ true), { useNonAdjustedEndPosition: true });
|
||||
this.replaceNode(sourceFile, ctr.body, createBlock(statements, /*multiLine*/ true));
|
||||
}
|
||||
|
||||
public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void {
|
||||
@@ -430,17 +445,11 @@ namespace ts.textChanges {
|
||||
// check if previous statement ends with semicolon
|
||||
// if not - insert semicolon to preserve the code from changing the meaning due to ASI
|
||||
if (sourceFile.text.charCodeAt(after.end - 1) !== CharacterCodes.semicolon) {
|
||||
this.changes.push({
|
||||
kind: ChangeKind.ReplaceWithSingleNode,
|
||||
sourceFile,
|
||||
options: {},
|
||||
range: { pos: after.end, end: after.end },
|
||||
node: createToken(SyntaxKind.SemicolonToken)
|
||||
});
|
||||
this.replaceRange(sourceFile, createTextRange(after.end), createToken(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
}
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, after, {});
|
||||
return this.replaceRange(sourceFile, { pos: endPosition, end: endPosition }, newNode, this.getInsertNodeAfterOptions(after));
|
||||
return this.replaceRange(sourceFile, createTextRange(endPosition), newNode, this.getInsertNodeAfterOptions(after));
|
||||
}
|
||||
|
||||
private getInsertNodeAfterOptions(node: Node): InsertNodeOptions {
|
||||
@@ -524,17 +533,9 @@ namespace ts.textChanges {
|
||||
startPos = getStartPositionOfLine(lineAndCharOfNextElement.line, sourceFile);
|
||||
}
|
||||
|
||||
this.changes.push({
|
||||
kind: ChangeKind.ReplaceWithSingleNode,
|
||||
sourceFile,
|
||||
range: { pos: startPos, end: containingList[index + 1].getStart(sourceFile) },
|
||||
node: newNode,
|
||||
options: {
|
||||
prefix,
|
||||
// write separator and leading trivia of the next element as suffix
|
||||
suffix: `${tokenToString(nextToken.kind)}${sourceFile.text.substring(nextToken.end, containingList[index + 1].getStart(sourceFile))}`
|
||||
}
|
||||
});
|
||||
// write separator and leading trivia of the next element as suffix
|
||||
const suffix = `${tokenToString(nextToken.kind)}${sourceFile.text.substring(nextToken.end, containingList[index + 1].getStart(sourceFile))}`;
|
||||
this.replaceRange(sourceFile, createTextRange(startPos, containingList[index + 1].getStart(sourceFile)), newNode, { prefix, suffix });
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -568,13 +569,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
if (multilineList) {
|
||||
// insert separator immediately following the 'after' node to preserve comments in trailing trivia
|
||||
this.changes.push({
|
||||
kind: ChangeKind.ReplaceWithSingleNode,
|
||||
sourceFile,
|
||||
range: { pos: end, end },
|
||||
node: createToken(separator),
|
||||
options: {}
|
||||
});
|
||||
this.replaceRange(sourceFile, createTextRange(end), createToken(separator));
|
||||
// use the same indentation as 'after' item
|
||||
const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(afterStartLinePosition, afterStart, sourceFile, this.formatContext.options);
|
||||
// insert element before the line break on the line that contains 'after' element
|
||||
@@ -582,22 +577,10 @@ namespace ts.textChanges {
|
||||
if (insertPos !== end && isLineBreak(sourceFile.text.charCodeAt(insertPos - 1))) {
|
||||
insertPos--;
|
||||
}
|
||||
this.changes.push({
|
||||
kind: ChangeKind.ReplaceWithSingleNode,
|
||||
sourceFile,
|
||||
range: { pos: insertPos, end: insertPos },
|
||||
node: newNode,
|
||||
options: { indentation, prefix: this.newLineCharacter }
|
||||
});
|
||||
this.replaceRange(sourceFile, createTextRange(insertPos), newNode, { indentation, prefix: this.newLineCharacter });
|
||||
}
|
||||
else {
|
||||
this.changes.push({
|
||||
kind: ChangeKind.ReplaceWithSingleNode,
|
||||
sourceFile,
|
||||
range: { pos: end, end },
|
||||
node: newNode,
|
||||
options: { prefix: `${tokenToString(separator)} ` }
|
||||
});
|
||||
this.replaceRange(sourceFile, createTextRange(end), newNode, { prefix: `${tokenToString(separator)} ` });
|
||||
}
|
||||
}
|
||||
return this;
|
||||
@@ -608,7 +591,7 @@ namespace ts.textChanges {
|
||||
const newCls = cls.kind === SyntaxKind.ClassDeclaration
|
||||
? updateClassDeclaration(cls, cls.decorators, cls.modifiers, cls.name, cls.typeParameters, cls.heritageClauses, members)
|
||||
: updateClassExpression(cls, cls.modifiers, cls.name, cls.typeParameters, cls.heritageClauses, members);
|
||||
this.replaceNode(sourceFile, cls, newCls, { useNonAdjustedEndPosition: true });
|
||||
this.replaceNode(sourceFile, cls, newCls);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -647,6 +630,9 @@ namespace ts.textChanges {
|
||||
if (change.kind === ChangeKind.Remove) {
|
||||
return "";
|
||||
}
|
||||
if (change.kind === ChangeKind.Text) {
|
||||
return change.text;
|
||||
}
|
||||
|
||||
const { options = {}, range: { pos } } = change;
|
||||
const format = (n: Node) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, validate);
|
||||
@@ -659,20 +645,18 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
/** Note: this may mutate `nodeIn`. */
|
||||
function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, options: ChangeNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText): string {
|
||||
function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText): string {
|
||||
const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter);
|
||||
if (validate) validate(node, text);
|
||||
const { options: formatOptions } = formatContext;
|
||||
const initialIndentation =
|
||||
options.indentation !== undefined
|
||||
? options.indentation
|
||||
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, options.prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos);
|
||||
const delta =
|
||||
options.delta !== undefined
|
||||
? options.delta
|
||||
: formatting.SmartIndenter.shouldIndentChildNode(formatContext.options, nodeIn)
|
||||
? (formatOptions.indentSize || 0)
|
||||
: 0;
|
||||
indentation !== undefined
|
||||
? indentation
|
||||
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos);
|
||||
if (delta === undefined) {
|
||||
delta = formatting.SmartIndenter.shouldIndentChildNode(formatContext.options, nodeIn) ? (formatOptions.indentSize || 0) : 0;
|
||||
}
|
||||
|
||||
const file: SourceFileLike = { text, getLineAndCharacterOfPosition(pos) { return getLineAndCharacterOfPosition(this, pos); } };
|
||||
const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, formatContext);
|
||||
return applyChanges(text, changes);
|
||||
@@ -896,4 +880,8 @@ namespace ts.textChanges {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isValidLocationToAddComment(sourceFile: SourceFile, position: number) {
|
||||
return !isInComment(sourceFile, position) && !isInString(sourceFile, position) && !isInTemplateString(sourceFile, position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +440,7 @@ namespace ts {
|
||||
* This may be omitted to indicate that the code fix can't be applied in a group.
|
||||
*/
|
||||
fixId?: {};
|
||||
fixAllDescription?: string;
|
||||
}
|
||||
|
||||
export interface CombinedCodeActions {
|
||||
|
||||
@@ -1216,6 +1216,10 @@ namespace ts {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function skipConstraint(type: Type): Type {
|
||||
return type.flags & TypeFlags.TypeParameter ? type.getConstraint() : type;
|
||||
}
|
||||
}
|
||||
|
||||
// Display-part writer helpers
|
||||
|
||||
Reference in New Issue
Block a user