mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'createTypeNode' of github.com:aozgaa/TypeScript into createTypeNode
This commit is contained in:
+1
-4
@@ -11,11 +11,7 @@ var ts = require("./lib/typescript");
|
||||
|
||||
// Variables
|
||||
var compilerDirectory = "src/compiler/";
|
||||
var servicesDirectory = "src/services/";
|
||||
var serverDirectory = "src/server/";
|
||||
var typingsInstallerDirectory = "src/server/typingsInstaller";
|
||||
var cancellationTokenDirectory = "src/server/cancellationToken";
|
||||
var watchGuardDirectory = "src/server/watchGuard";
|
||||
var harnessDirectory = "src/harness/";
|
||||
var libraryDirectory = "src/lib/";
|
||||
var scriptsDirectory = "scripts/";
|
||||
@@ -131,6 +127,7 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"matchFiles.ts",
|
||||
"initializeTSConfig.ts",
|
||||
"printer.ts",
|
||||
"textChanges.ts",
|
||||
"transform.ts",
|
||||
"customTransforms.ts",
|
||||
].map(function (f) {
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace ts {
|
||||
getParameterType: getTypeAtPosition,
|
||||
getReturnTypeOfSignature,
|
||||
getNonNullableType,
|
||||
createTypeNode,
|
||||
getSymbolsInScope: (location, meaning) => {
|
||||
location = getParseTreeNode(location);
|
||||
return location ? getSymbolsInScope(location, meaning) : [];
|
||||
@@ -2188,6 +2189,221 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function createTypeNode(type: Type) {
|
||||
let undefinedArgumentIsError = true;
|
||||
let encounteredError = false;
|
||||
let checkAlias = true;
|
||||
|
||||
return createTypeNodeWorker(type);
|
||||
|
||||
function createTypeNodeWorker(type: Type): TypeNode {
|
||||
if (!type) {
|
||||
if (undefinedArgumentIsError) { encounteredError = true; }
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (checkAlias && type.aliasSymbol) {
|
||||
const name = getNameOfSymbol(type.aliasSymbol);
|
||||
const typeArguments = mapToTypeNodeArray(type.aliasTypeArguments);
|
||||
return createTypeReferenceNode(createIdentifier(name), typeArguments);
|
||||
}
|
||||
checkAlias = false;
|
||||
|
||||
if (type.flags & TypeFlags.Any) {
|
||||
// TODO: add other case where type ends up being `any`.
|
||||
return createKeywordTypeNode(SyntaxKind.StringKeyword);
|
||||
}
|
||||
if (type.flags & TypeFlags.String) {
|
||||
return createKeywordTypeNode(SyntaxKind.StringKeyword);
|
||||
}
|
||||
if (type.flags & TypeFlags.Number) {
|
||||
return createKeywordTypeNode(SyntaxKind.NumberKeyword);
|
||||
}
|
||||
if(type.flags & TypeFlags.Boolean) {
|
||||
// TODO: this is probably x: boolean. How do we deal with x: true ?
|
||||
return createKeywordTypeNode(SyntaxKind.BooleanKeyword);
|
||||
}
|
||||
if (type.flags & (TypeFlags.StringLiteral)) {
|
||||
return createLiteralTypeNode((createLiteral((<LiteralType>type).text)));
|
||||
}
|
||||
if (type.flags & (TypeFlags.NumberLiteral)) {
|
||||
return createLiteralTypeNode((createNumericLiteral((<LiteralType>type).text)));
|
||||
}
|
||||
if (type.flags & TypeFlags.Void) {
|
||||
return createKeywordTypeNode(SyntaxKind.VoidKeyword);
|
||||
}
|
||||
if (type.flags & TypeFlags.Undefined) {
|
||||
return createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
|
||||
}
|
||||
if (type.flags & TypeFlags.Null) {
|
||||
return createKeywordTypeNode(SyntaxKind.NullKeyword);
|
||||
}
|
||||
if (type.flags & TypeFlags.Never) {
|
||||
return createKeywordTypeNode(SyntaxKind.NeverKeyword);
|
||||
}
|
||||
if (type.flags & TypeFlags.Enum) {
|
||||
throw new Error("enum not implemented");
|
||||
}
|
||||
if (type.flags & TypeFlags.ESSymbol) {
|
||||
throw new Error("ESSymbol not implemented");
|
||||
}
|
||||
if (type.flags & TypeFlags.TypeParameter) {
|
||||
if ((<TypeParameter>type).isThisType) {
|
||||
return createThis();
|
||||
}
|
||||
throw new Error("Type Parameter declarations only handled in other worker.");
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, mapToTypeNodeArray((type as UnionType).types));
|
||||
}
|
||||
if (type.flags & TypeFlags.Intersection) {
|
||||
return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types));
|
||||
}
|
||||
if (type.flags & TypeFlags.Index) {
|
||||
throw new Error("index not implemented");
|
||||
}
|
||||
if (type.flags & TypeFlags.IndexedAccess) {
|
||||
throw new Error("indexed access not implemented");
|
||||
}
|
||||
|
||||
const objectFlags = getObjectFlags(type);
|
||||
|
||||
if (objectFlags & ObjectFlags.ClassOrInterface) {
|
||||
Debug.assert(!!(type.flags & TypeFlags.Object));
|
||||
const name = getNameOfSymbol(type.symbol);
|
||||
// TODO: handle type arguments.
|
||||
// TODO: handle anonymous classes.
|
||||
return createTypeReferenceNode(name);
|
||||
}
|
||||
|
||||
if (objectFlags & ObjectFlags.Reference) {
|
||||
Debug.assert(!!(type.flags & TypeFlags.Object));
|
||||
// and vice versa.
|
||||
// this case includes tuple types
|
||||
// TODO: test empty tuples, see if they are coherent.
|
||||
const typeArguments = (type as TypeReference).typeArguments || emptyArray;
|
||||
return createTupleTypeNode(mapToTypeNodeArray(typeArguments))
|
||||
}
|
||||
|
||||
// keyword types
|
||||
// this type node
|
||||
// function type node
|
||||
// constructor type node
|
||||
// type reference node
|
||||
// type predicate node - is Foo (for return types)
|
||||
// type query node -- typeof number
|
||||
// type literal node (like object literal)
|
||||
// array type
|
||||
// tuple type
|
||||
// union type
|
||||
// might need parens
|
||||
// intersection type
|
||||
// Type operator node (eg (ie?): keyof T)
|
||||
// IndexedAccess Type Node
|
||||
// mapped type node
|
||||
// literal type node
|
||||
|
||||
// if (inTypeAlias && type.aliasSymbol) {
|
||||
// return isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/false).accessibility === SymbolAccessibility.Accessible
|
||||
// && (!type.aliasTypeArguments || allTypesVisible(type.aliasTypeArguments));
|
||||
// }
|
||||
// const typeSymbolAccessibility = type.symbol && isSymbolAccessible(type.symbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility;
|
||||
// if (type.flags & TypeFlags.TypeParameter) {
|
||||
// if (inObjectLiteral && (type as TypeParameter).isThisType) {
|
||||
// return false;
|
||||
// }
|
||||
// const constraint = getConstraintFromTypeParameter((<TypeParameter>type));
|
||||
// return typeSymbolAccessibility === SymbolAccessibility.Accessible
|
||||
// && (!constraint || isTypeAccessibleWorker(constraint, inObjectLiteral, /*inTypeAlias*/false));
|
||||
// }
|
||||
// const objectFlags = getObjectFlags(type);
|
||||
// if (objectFlags & ObjectFlags.ClassOrInterface) {
|
||||
// // If type is a class or interface type that wasn't hit by the isSymbolAccessible check above,
|
||||
// // type must be an anonymous class or interface.
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (objectFlags & ObjectFlags.Mapped) {
|
||||
Debug.assert(!!(type.flags & TypeFlags.Object));
|
||||
// const typeParameter = getTypeParameterFromMappedType(<MappedType>type);
|
||||
// const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
|
||||
// const templateType = getTemplateTypeFromMappedType(<MappedType>type);
|
||||
throw new Error("Mapped types not implemented");
|
||||
}
|
||||
|
||||
if (objectFlags & ObjectFlags.Anonymous) {
|
||||
Debug.assert(!!(type.flags & TypeFlags.Object));
|
||||
// The type is an object literal type.
|
||||
if (!type.symbol) {
|
||||
// Anonymous types without symbols are literals.
|
||||
|
||||
|
||||
// mapToTypeDeclarationsArray(type)
|
||||
throw new Error("unknown case.");
|
||||
}
|
||||
|
||||
const members = type.symbol.members;
|
||||
const newMembers: TypeElement[] = [];
|
||||
memberLoop: for(const key in members){
|
||||
const oldMember = members.get(key);
|
||||
const name = getNameOfSymbol(oldMember);
|
||||
const oldDeclaration = oldMember.declarations && oldMember.declarations[0] as TypeElement;
|
||||
if(!oldDeclaration) {
|
||||
continue memberLoop;
|
||||
}
|
||||
|
||||
const kind = oldDeclaration.kind;
|
||||
|
||||
switch (kind) {
|
||||
case SyntaxKind.PropertySignature:
|
||||
const optional = !!oldDeclaration.questionToken;
|
||||
newMembers.push(createPropertySignature(
|
||||
createIdentifier(name)
|
||||
, optional ? createToken(SyntaxKind.QuestionToken) : undefined
|
||||
, createTypeNode(getTypeOfSymbol(oldMember))));
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.IndexSignature:
|
||||
default:
|
||||
throw new Error("type literal constituent not implemented.");
|
||||
}
|
||||
}
|
||||
return createTypeLiteralNode(newMembers);
|
||||
}
|
||||
|
||||
Debug.fail("Should be unreachable.");
|
||||
|
||||
// function createTypeParameterDeclarationFromType(type: Type): TypeParameterDeclaration {
|
||||
// if (!type) {
|
||||
// if (undefinedArgumentIsError) { encounteredError = true; }
|
||||
// return undefined;
|
||||
// }
|
||||
// if (type.flags & TypeFlags.TypeParameter) {
|
||||
// const constraint = createTypeNodeWorker(getConstraintFromTypeParameter(<TypeParameter>type)) as TypeNode;
|
||||
// const defaultParameter = createTypeNodeWorker(getDefaultFromTypeParameter(<TypeParameter>type)) as TypeNode;
|
||||
// if (!type.symbol) {
|
||||
// encounteredError = true;
|
||||
// throw new Error("No symbol for type parameter so can't get name");
|
||||
// }
|
||||
// const name = getNameOfSymbol(type.symbol);
|
||||
// return createTypeParameterDeclaration(name, constraint, defaultParameter);
|
||||
// }
|
||||
// throw new Error("type declarations not implemented.");
|
||||
// }
|
||||
|
||||
/** Note that mapToTypeNodeArray(undefined) === undefined. */
|
||||
function mapToTypeNodeArray(types: Type[]): NodeArray<TypeNode> {
|
||||
return asNodeArray(types && types.map(createTypeNodeWorker) as TypeNode[]);
|
||||
}
|
||||
|
||||
// /** Note that mapToTypeNodeArray(undefined) === undefined. */
|
||||
// function mapToTypeParameterArray(types: Type[]): NodeArray<TypeNode> {
|
||||
// return asNodeArray(types && types.map(createTypeParameterDeclarationFromType) as TypeNode[]);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string {
|
||||
const writer = getSingleLineStringWriter();
|
||||
getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags);
|
||||
@@ -6955,6 +7171,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// export function synthesizeTypeNode(type: Type, enclosingDeclaration: Node): TypeNode {
|
||||
// throw new Error("Not implemented" + enclosingDeclaration);
|
||||
// }
|
||||
|
||||
function instantiateList<T>(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] {
|
||||
if (items && items.length) {
|
||||
const result: T[] = [];
|
||||
|
||||
+31
-2
@@ -199,6 +199,8 @@ namespace ts {
|
||||
onEmitHelpers,
|
||||
onSetSourceFile,
|
||||
substituteNode,
|
||||
onBeforeEmitNodeArray,
|
||||
onAfterEmitNodeArray
|
||||
} = handlers;
|
||||
|
||||
const newLine = getNewLineCharacter(printerOptions);
|
||||
@@ -631,6 +633,11 @@ namespace ts {
|
||||
if (isExpression(node)) {
|
||||
return pipelineEmitExpression(trySubstituteNode(EmitHint.Expression, node));
|
||||
}
|
||||
|
||||
if (isToken(node)) {
|
||||
writeTokenText(kind);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function pipelineEmitExpression(node: Node): void {
|
||||
@@ -1553,6 +1560,10 @@ namespace ts {
|
||||
emitSignatureAndBody(node, emitSignatureHead);
|
||||
}
|
||||
|
||||
function emitBlockCallback(_hint: EmitHint, body: Node): void {
|
||||
emitBlockFunctionBody(<Block>body);
|
||||
}
|
||||
|
||||
function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) {
|
||||
const body = node.body;
|
||||
if (body) {
|
||||
@@ -1564,12 +1575,22 @@ namespace ts {
|
||||
|
||||
if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) {
|
||||
emitSignatureHead(node);
|
||||
emitBlockFunctionBody(body);
|
||||
if (onEmitNode) {
|
||||
onEmitNode(EmitHint.Unspecified, body, emitBlockCallback);
|
||||
}
|
||||
else {
|
||||
emitBlockFunctionBody(body);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pushNameGenerationScope();
|
||||
emitSignatureHead(node);
|
||||
emitBlockFunctionBody(body);
|
||||
if (onEmitNode) {
|
||||
onEmitNode(EmitHint.Unspecified, body, emitBlockCallback);
|
||||
}
|
||||
else {
|
||||
emitBlockFunctionBody(body);
|
||||
}
|
||||
popNameGenerationScope();
|
||||
}
|
||||
|
||||
@@ -2200,6 +2221,10 @@ namespace ts {
|
||||
write(getOpeningBracket(format));
|
||||
}
|
||||
|
||||
if (onBeforeEmitNodeArray) {
|
||||
onBeforeEmitNodeArray(children);
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
// Write a line terminator if the parent node was multi-line
|
||||
if (format & ListFormat.MultiLine) {
|
||||
@@ -2315,6 +2340,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
if (onAfterEmitNodeArray) {
|
||||
onAfterEmitNodeArray(children);
|
||||
}
|
||||
|
||||
if (format & ListFormat.BracketsMask) {
|
||||
write(getClosingBracket(format));
|
||||
}
|
||||
|
||||
+153
-4
@@ -45,7 +45,10 @@ namespace ts {
|
||||
* Creates a shallow, memberwise clone of a node with no source map location.
|
||||
*/
|
||||
/* @internal */
|
||||
export function getSynthesizedClone<T extends Node>(node: T): T {
|
||||
export function getSynthesizedClone<T extends Node>(node: T | undefined): T {
|
||||
if(node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
|
||||
// the original node. We also need to exclude specific properties and only include own-
|
||||
// properties (to skip members already defined on the shared prototype).
|
||||
@@ -64,6 +67,14 @@ namespace ts {
|
||||
return clone;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return getSynthesizedClone(visitEachChild(node, getSynthesizedClone, nullTransformationContext));
|
||||
}
|
||||
|
||||
// Literals
|
||||
|
||||
export function createLiteral(value: string): StringLiteral;
|
||||
@@ -170,11 +181,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function createThis() {
|
||||
return <PrimaryExpression>createSynthesizedNode(SyntaxKind.ThisKeyword);
|
||||
return <PrimaryExpression & TypeNode>createSynthesizedNode(SyntaxKind.ThisKeyword);
|
||||
}
|
||||
|
||||
export function createNull() {
|
||||
return <PrimaryExpression>createSynthesizedNode(SyntaxKind.NullKeyword);
|
||||
return <PrimaryExpression & TypeNode>createSynthesizedNode(SyntaxKind.NullKeyword);
|
||||
}
|
||||
|
||||
export function createTrue() {
|
||||
@@ -213,8 +224,146 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
// Type Elements
|
||||
|
||||
export function createPropertySignature(name: PropertyName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): PropertySignature {
|
||||
const propertySignature = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature;
|
||||
propertySignature.name = name;
|
||||
propertySignature.questionToken = questionToken;
|
||||
propertySignature.type = type;
|
||||
propertySignature.initializer = initializer;
|
||||
return propertySignature;
|
||||
}
|
||||
|
||||
export function createConstructSignature() {
|
||||
throw new Error("not implemented.");
|
||||
}
|
||||
|
||||
// Types
|
||||
|
||||
export function createKeywordTypeNode(kind: KeywordTypeNode["kind"]): KeywordTypeNode {
|
||||
return <KeywordTypeNode>createSynthesizedNode(kind);
|
||||
}
|
||||
|
||||
export function createLiteralTypeNode(literal: Expression) {
|
||||
const literalTypeNode = createSynthesizedNode(SyntaxKind.LiteralType) as LiteralTypeNode;
|
||||
literalTypeNode.literal = literal;
|
||||
return literalTypeNode;
|
||||
}
|
||||
|
||||
export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Expression) {
|
||||
return node.literal !== literal
|
||||
? updateNode(createLiteralTypeNode(literal), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
// TODO: handle qualified names, ie EntityName's.
|
||||
export function createTypeReferenceNode(typeName: string | Identifier, typeArguments?: NodeArray<TypeNode>) {
|
||||
const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode;
|
||||
typeReference.typeName = asName(typeName);
|
||||
typeReference.typeArguments = typeArguments;
|
||||
return typeReference;
|
||||
}
|
||||
|
||||
export function updateTypeReferenceNode(node: TypeReferenceNode, typeName: Identifier, typeArguments?: NodeArray<TypeNode>) {
|
||||
return node.typeName !== typeName
|
||||
|| node.typeArguments !== typeArguments
|
||||
? updateNode(createTypeReferenceNode(typeName, typeArguments), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType, types: TypeNode[]): UnionTypeNode;
|
||||
export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.IntersectionType, types: TypeNode[]): IntersectionTypeNode;
|
||||
export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]): UnionOrIntersectionTypeNode;
|
||||
export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]): UnionOrIntersectionTypeNode {
|
||||
const unionTypeNode = createSynthesizedNode(kind) as UnionTypeNode | IntersectionTypeNode;
|
||||
unionTypeNode.types = asNodeArray(types);
|
||||
return unionTypeNode;
|
||||
}
|
||||
|
||||
export function updateUnionOrIntersectionTypeNode(node: UnionOrIntersectionTypeNode, types: NodeArray<TypeNode>) {
|
||||
return node.types !== types
|
||||
? updateNode(createUnionOrIntersectionTypeNode(node.kind, types), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createTypeLiteralNode(members: TypeElement[]) {
|
||||
const typeLiteralNode = createSynthesizedNode(SyntaxKind.LiteralType) as TypeLiteralNode;
|
||||
typeLiteralNode.members = asNodeArray(members);
|
||||
return typeLiteralNode;
|
||||
}
|
||||
|
||||
export function updateTypeLiteralNode(node: TypeLiteralNode, members: NodeArray<TypeElement>) {
|
||||
return node.members !== members
|
||||
? updateNode(createTypeLiteralNode(members), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createTupleTypeNode(elementTypes: TypeNode[]) {
|
||||
const tupleTypeNode = createSynthesizedNode(SyntaxKind.TupleType) as TupleTypeNode;
|
||||
tupleTypeNode.elementTypes = asNodeArray(elementTypes);
|
||||
return tupleTypeNode;
|
||||
}
|
||||
|
||||
export function updateTypleTypeNode(node: TupleTypeNode, elementTypes: TypeNode[]) {
|
||||
return node.elementTypes !== elementTypes
|
||||
? updateNode(createTupleTypeNode(elementTypes), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
// Type Declarations
|
||||
|
||||
export function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultParameter?: TypeNode) {
|
||||
const typeParameter = createSynthesizedNode(SyntaxKind.TypeParameter) as TypeParameterDeclaration;
|
||||
typeParameter.name = asName(name);
|
||||
typeParameter.constraint = constraint;
|
||||
typeParameter.default = defaultParameter;
|
||||
|
||||
return typeParameter;
|
||||
}
|
||||
|
||||
export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint?: TypeNode, defaultParameter?: TypeNode) {
|
||||
return node.name !== name
|
||||
|| node.constraint !== constraint
|
||||
|| node.default !== defaultParameter
|
||||
? updateNode(createTypeParameterDeclaration(name, constraint, defaultParameter), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
// Signature elements
|
||||
|
||||
/** Note, can also be used to construct index signatures. */
|
||||
export function createSignature(kind: SyntaxKind, parameters: NodeArray<ParameterDeclaration>, name?: PropertyName, typeParameters?: NodeArray<TypeParameterDeclaration>, returnType?: TypeNode): SignatureDeclaration {
|
||||
const signature = createSynthesizedNode(kind) as SignatureDeclaration;
|
||||
signature.parameters = parameters;
|
||||
signature.name = name;
|
||||
signature.typeParameters = typeParameters;
|
||||
signature.type = returnType;
|
||||
return signature;
|
||||
}
|
||||
|
||||
// TODO: check usage of name...
|
||||
// TODO: create entry in visitor.ts
|
||||
export function createIndexSignatureDeclaration(parameters: ParameterDeclaration[], type: TypeNode, decorators?: Decorator[], modifiers?: Modifier[]): IndexSignatureDeclaration {
|
||||
const indexSignature = createSynthesizedNode(SyntaxKind.IndexSignature) as IndexSignatureDeclaration;
|
||||
// indexSignature.name = asName(name);
|
||||
// type parameters
|
||||
indexSignature.parameters = asNodeArray(parameters);
|
||||
indexSignature.type = type;
|
||||
indexSignature.decorators = asNodeArray(decorators);
|
||||
indexSignature.modifiers = asNodeArray(modifiers);
|
||||
return indexSignature;
|
||||
}
|
||||
|
||||
export function updateIndexSignatureDeclaration(node: IndexSignatureDeclaration, parameters: ParameterDeclaration[], type: TypeNode, decorators?: Decorator[], modifiers?: Modifier[]) {
|
||||
return node.parameters !== parameters
|
||||
|| node.type !== type
|
||||
|| node.decorators !== decorators
|
||||
|| node.modifiers !== modifiers
|
||||
? updateNode(createIndexSignatureDeclaration(parameters, type, decorators, modifiers), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createParameter(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression) {
|
||||
const node = <ParameterDeclaration>createSynthesizedNode(SyntaxKind.Parameter);
|
||||
node.decorators = asNodeArray(decorators);
|
||||
@@ -1797,7 +1946,7 @@ namespace ts {
|
||||
return typeof value === "string" || typeof value === "number" ? createLiteral(value) : value;
|
||||
}
|
||||
|
||||
function asNodeArray<T extends Node>(array: T[] | undefined): NodeArray<T> | undefined {
|
||||
export function asNodeArray<T extends Node>(array: T[] | undefined): NodeArray<T> | undefined {
|
||||
return array ? createNodeArray(array) : undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getLineStarts(sourceFile: SourceFile): number[] {
|
||||
export function getLineStarts(sourceFile: SourceFileLike): number[] {
|
||||
return sourceFile.lineMap || (sourceFile.lineMap = computeLineStarts(sourceFile.text));
|
||||
}
|
||||
|
||||
|
||||
+21
-4
@@ -818,7 +818,7 @@ namespace ts {
|
||||
body?: FunctionBody;
|
||||
}
|
||||
|
||||
// For when we encounter a semicolon in a class declaration. ES6 allows these as class elements.
|
||||
/** For when we encounter a semicolon in a class declaration. ES6 allows these as class elements.*/
|
||||
export interface SemicolonClassElement extends ClassElement {
|
||||
kind: SyntaxKind.SemicolonClassElement;
|
||||
}
|
||||
@@ -856,7 +856,10 @@ namespace ts {
|
||||
| SyntaxKind.BooleanKeyword
|
||||
| SyntaxKind.StringKeyword
|
||||
| SyntaxKind.SymbolKeyword
|
||||
| SyntaxKind.VoidKeyword;
|
||||
| SyntaxKind.VoidKeyword
|
||||
| SyntaxKind.UndefinedKeyword
|
||||
| SyntaxKind.NullKeyword
|
||||
| SyntaxKind.NeverKeyword;
|
||||
}
|
||||
|
||||
export interface ThisTypeNode extends TypeNode {
|
||||
@@ -892,7 +895,7 @@ namespace ts {
|
||||
exprName: EntityName;
|
||||
}
|
||||
|
||||
// A TypeLiteral is the declaration node for an anonymous symbol.
|
||||
/** A TypeLiteral is the declaration node for an anonymous symbol. */
|
||||
export interface TypeLiteralNode extends TypeNode, Declaration {
|
||||
kind: SyntaxKind.TypeLiteral;
|
||||
members: NodeArray<TypeElement>;
|
||||
@@ -2205,6 +2208,16 @@ namespace ts {
|
||||
name: string;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
/**
|
||||
* Subset of properties from SourceFile that are used in multiple utility functions
|
||||
*/
|
||||
export interface SourceFileLike {
|
||||
readonly text: string;
|
||||
lineMap: number[];
|
||||
}
|
||||
|
||||
|
||||
// Source files are declarations when they are external modules.
|
||||
export interface SourceFile extends Declaration {
|
||||
kind: SyntaxKind.SourceFile;
|
||||
@@ -2453,6 +2466,8 @@ namespace ts {
|
||||
*/
|
||||
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
|
||||
getNonNullableType(type: Type): Type;
|
||||
/** Note that the resulting type node cannot be checked. */
|
||||
createTypeNode(type: Type): TypeNode;
|
||||
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
|
||||
getSymbolAtLocation(node: Node): Symbol;
|
||||
@@ -4051,7 +4066,7 @@ namespace ts {
|
||||
export type Transformer<T extends Node> = (node: T) => T;
|
||||
|
||||
/**
|
||||
* A function that accepts and possible transforms a node.
|
||||
* A function that accepts and possibly transforms a node.
|
||||
*/
|
||||
export type Visitor = (node: Node) => VisitResult<Node>;
|
||||
|
||||
@@ -4132,6 +4147,8 @@ namespace ts {
|
||||
/*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void;
|
||||
/*@internal*/ onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void;
|
||||
/*@internal*/ onSetSourceFile?: (node: SourceFile) => void;
|
||||
/*@internal*/ onBeforeEmitNodeArray?: (nodes: NodeArray<any>) => void;
|
||||
/*@internal*/ onAfterEmitNodeArray?: (nodes: NodeArray<any>) => void;
|
||||
}
|
||||
|
||||
export interface PrinterOptions {
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getStartPositionOfLine(line: number, sourceFile: SourceFile): number {
|
||||
export function getStartPositionOfLine(line: number, sourceFile: SourceFileLike): number {
|
||||
Debug.assert(line >= 0);
|
||||
return getLineStarts(sourceFile)[line];
|
||||
}
|
||||
@@ -204,7 +204,7 @@ namespace ts {
|
||||
return value !== undefined;
|
||||
}
|
||||
|
||||
export function getEndLinePosition(line: number, sourceFile: SourceFile): number {
|
||||
export function getEndLinePosition(line: number, sourceFile: SourceFileLike): number {
|
||||
Debug.assert(line >= 0);
|
||||
const lineStarts = getLineStarts(sourceFile);
|
||||
|
||||
@@ -255,7 +255,11 @@ namespace ts {
|
||||
return !nodeIsMissing(node);
|
||||
}
|
||||
|
||||
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile, includeJsDoc?: boolean): number {
|
||||
export function isToken(n: Node): boolean {
|
||||
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
|
||||
}
|
||||
|
||||
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: boolean): number {
|
||||
// With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
|
||||
// want to skip trivia because this will launch us forward to the next token.
|
||||
if (nodeIsMissing(node)) {
|
||||
@@ -289,7 +293,7 @@ namespace ts {
|
||||
return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
|
||||
}
|
||||
|
||||
export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
|
||||
export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFileLike): number {
|
||||
if (nodeIsMissing(node) || !node.decorators) {
|
||||
return getTokenPosOfNode(node, sourceFile);
|
||||
}
|
||||
@@ -2491,7 +2495,7 @@ namespace ts {
|
||||
return indentStrings[1].length;
|
||||
}
|
||||
|
||||
export function createTextWriter(newLine: String): EmitTextWriter {
|
||||
export function createTextWriter(newLine: string): EmitTextWriter {
|
||||
let output: string;
|
||||
let indent: number;
|
||||
let lineStart: boolean;
|
||||
|
||||
+177
-82
@@ -3,6 +3,28 @@
|
||||
/// <reference path="utilities.ts" />
|
||||
|
||||
namespace ts {
|
||||
|
||||
|
||||
export const nullTransformationContext: TransformationContext = {
|
||||
enableEmitNotification: noop,
|
||||
enableSubstitution: noop,
|
||||
endLexicalEnvironment: () => undefined,
|
||||
getCompilerOptions: notImplemented,
|
||||
getEmitHost: notImplemented,
|
||||
getEmitResolver: notImplemented,
|
||||
hoistFunctionDeclaration: noop,
|
||||
hoistVariableDeclaration: noop,
|
||||
isEmitNotificationEnabled: notImplemented,
|
||||
isSubstitutionEnabled: notImplemented,
|
||||
onEmitNode: noop,
|
||||
onSubstituteNode: notImplemented,
|
||||
readEmitHelpers: notImplemented,
|
||||
requestEmitHelper: noop,
|
||||
resumeLexicalEnvironment: noop,
|
||||
startLexicalEnvironment: noop,
|
||||
suspendLexicalEnvironment: noop
|
||||
};
|
||||
|
||||
/**
|
||||
* Visits a Node using the supplied visitor, possibly returning a new Node in its place.
|
||||
*
|
||||
@@ -154,9 +176,9 @@ namespace ts {
|
||||
* Starts a new lexical environment and visits a parameter list, suspending the lexical
|
||||
* environment upon completion.
|
||||
*/
|
||||
export function visitParameterList(nodes: NodeArray<ParameterDeclaration>, visitor: Visitor, context: TransformationContext) {
|
||||
export function visitParameterList(nodes: NodeArray<ParameterDeclaration>, visitor: Visitor, context: TransformationContext, nodesVisitor = visitNodes) {
|
||||
context.startLexicalEnvironment();
|
||||
const updated = visitNodes(nodes, visitor, isParameterDeclaration);
|
||||
const updated = nodesVisitor(nodes, visitor, isParameterDeclaration);
|
||||
context.suspendLexicalEnvironment();
|
||||
return updated;
|
||||
}
|
||||
@@ -204,24 +226,20 @@ namespace ts {
|
||||
* @param visitor The callback used to visit each child.
|
||||
* @param context A lexical environment context for the visitor.
|
||||
*/
|
||||
export function visitEachChild<T extends Node>(node: T | undefined, visitor: Visitor, context: TransformationContext): T | undefined;
|
||||
export function visitEachChild<T extends Node>(node: T | undefined, visitor: Visitor, context: TransformationContext, nodesVisitor?: typeof visitNodes): T | undefined;
|
||||
|
||||
export function visitEachChild(node: Node, visitor: Visitor, context: TransformationContext): Node {
|
||||
export function visitEachChild(node: Node, visitor: Visitor, context: TransformationContext, nodesVisitor = visitNodes): Node {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const kind = node.kind;
|
||||
|
||||
// No need to visit nodes with no children.
|
||||
if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// We do not yet support types.
|
||||
if ((kind >= SyntaxKind.TypePredicate && kind <= SyntaxKind.LiteralType)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SemicolonClassElement:
|
||||
case SyntaxKind.EmptyStatement:
|
||||
@@ -241,10 +259,17 @@ namespace ts {
|
||||
visitNode((<ComputedPropertyName>node).expression, visitor, isExpression));
|
||||
|
||||
// Signature elements
|
||||
case SyntaxKind.IndexSignature:
|
||||
return updateIndexSignatureDeclaration(<IndexSignatureDeclaration>node
|
||||
, nodesVisitor((<IndexSignatureDeclaration>node).parameters, visitor)
|
||||
, visitNode((<IndexSignatureDeclaration>node).type, visitor)
|
||||
, nodesVisitor((<IndexSignatureDeclaration>node).decorators, visitor, isDecorator)
|
||||
, nodesVisitor((<IndexSignatureDeclaration>node).modifiers, visitor, isModifier));
|
||||
|
||||
case SyntaxKind.Parameter:
|
||||
return updateParameter(<ParameterDeclaration>node,
|
||||
visitNodes((<ParameterDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ParameterDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ParameterDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ParameterDeclaration>node).modifiers, visitor, isModifier),
|
||||
(<ParameterDeclaration>node).dotDotDotToken,
|
||||
visitNode((<ParameterDeclaration>node).name, visitor, isBindingName),
|
||||
visitNode((<ParameterDeclaration>node).type, visitor, isTypeNode),
|
||||
@@ -254,58 +279,127 @@ namespace ts {
|
||||
return updateDecorator(<Decorator>node,
|
||||
visitNode((<Decorator>node).expression, visitor, isExpression));
|
||||
|
||||
// Type member
|
||||
// Keyword Types
|
||||
|
||||
case SyntaxKind.AnyKeyword:
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
return node;
|
||||
|
||||
// Types
|
||||
|
||||
case SyntaxKind.TypePredicate:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.TypeReference:
|
||||
return updateTypeReferenceNode(<TypeReferenceNode>node
|
||||
, visitNode((<TypeReferenceNode>node).typeName as Identifier, visitor)
|
||||
, nodesVisitor((<TypeReferenceNode>node).typeArguments, visitor)
|
||||
);
|
||||
case SyntaxKind.FunctionType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.ConstructorType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.TypeQuery:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.TypeLiteral:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.ArrayType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.TupleType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.UnionType:
|
||||
case SyntaxKind.IntersectionType:
|
||||
return updateUnionOrIntersectionTypeNode(<UnionOrIntersectionTypeNode>node
|
||||
, nodesVisitor((<UnionOrIntersectionTypeNode>node).types, visitor, isTypeNode));
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.ThisType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.TypeOperator:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.MappedType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
case SyntaxKind.LiteralType:
|
||||
throw new Error("reached unsupported type in visitor.");
|
||||
|
||||
// Type Declarations
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
return updateTypeParameterDeclaration(<TypeParameterDeclaration>node
|
||||
, visitNode((<TypeParameterDeclaration>node).name, visitor, isIdentifier)
|
||||
, visitNode((<TypeParameterDeclaration>node).constraint, visitor, isTypeNode)
|
||||
, visitNode((<TypeParameterDeclaration>node).default, visitor, isTypeNode));
|
||||
|
||||
// Type members
|
||||
|
||||
case SyntaxKind.IndexSignature:
|
||||
updateIndexSignatureDeclaration(<IndexSignatureDeclaration>node
|
||||
, nodesVisitor((<IndexSignatureDeclaration>node).parameters, visitor, isParameter)
|
||||
, visitNode((<IndexSignatureDeclaration>node).type, visitor, isTypeNode)
|
||||
, nodesVisitor((<IndexSignatureDeclaration>node).decorators, visitor, isDecorator)
|
||||
, nodesVisitor((<IndexSignatureDeclaration>node).modifiers, visitor, isModifier));
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
return updateProperty(<PropertyDeclaration>node,
|
||||
visitNodes((<PropertyDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<PropertyDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<PropertyDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<PropertyDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<PropertyDeclaration>node).name, visitor, isPropertyName),
|
||||
visitNode((<PropertyDeclaration>node).type, visitor, isTypeNode),
|
||||
visitNode((<PropertyDeclaration>node).initializer, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
return updateMethod(<MethodDeclaration>node,
|
||||
visitNodes((<MethodDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<MethodDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<MethodDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<MethodDeclaration>node).modifiers, visitor, isModifier),
|
||||
(<MethodDeclaration>node).asteriskToken,
|
||||
visitNode((<MethodDeclaration>node).name, visitor, isPropertyName),
|
||||
visitNodes((<MethodDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<MethodDeclaration>node).parameters, visitor, context),
|
||||
nodesVisitor((<MethodDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<MethodDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<MethodDeclaration>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<MethodDeclaration>node).body, visitor, context));
|
||||
|
||||
case SyntaxKind.Constructor:
|
||||
return updateConstructor(<ConstructorDeclaration>node,
|
||||
visitNodes((<ConstructorDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ConstructorDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitParameterList((<ConstructorDeclaration>node).parameters, visitor, context),
|
||||
nodesVisitor((<ConstructorDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ConstructorDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitParameterList((<ConstructorDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitFunctionBody((<ConstructorDeclaration>node).body, visitor, context));
|
||||
|
||||
case SyntaxKind.GetAccessor:
|
||||
return updateGetAccessor(<GetAccessorDeclaration>node,
|
||||
visitNodes((<GetAccessorDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<GetAccessorDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<GetAccessorDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<GetAccessorDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<GetAccessorDeclaration>node).name, visitor, isPropertyName),
|
||||
visitParameterList((<GetAccessorDeclaration>node).parameters, visitor, context),
|
||||
visitParameterList((<GetAccessorDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<GetAccessorDeclaration>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<GetAccessorDeclaration>node).body, visitor, context));
|
||||
|
||||
case SyntaxKind.SetAccessor:
|
||||
return updateSetAccessor(<SetAccessorDeclaration>node,
|
||||
visitNodes((<SetAccessorDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<SetAccessorDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<SetAccessorDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<SetAccessorDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<SetAccessorDeclaration>node).name, visitor, isPropertyName),
|
||||
visitParameterList((<SetAccessorDeclaration>node).parameters, visitor, context),
|
||||
visitParameterList((<SetAccessorDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitFunctionBody((<SetAccessorDeclaration>node).body, visitor, context));
|
||||
|
||||
// Binding patterns
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
return updateObjectBindingPattern(<ObjectBindingPattern>node,
|
||||
visitNodes((<ObjectBindingPattern>node).elements, visitor, isBindingElement));
|
||||
nodesVisitor((<ObjectBindingPattern>node).elements, visitor, isBindingElement));
|
||||
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return updateArrayBindingPattern(<ArrayBindingPattern>node,
|
||||
visitNodes((<ArrayBindingPattern>node).elements, visitor, isArrayBindingElement));
|
||||
nodesVisitor((<ArrayBindingPattern>node).elements, visitor, isArrayBindingElement));
|
||||
|
||||
case SyntaxKind.BindingElement:
|
||||
return updateBindingElement(<BindingElement>node,
|
||||
@@ -317,11 +411,11 @@ namespace ts {
|
||||
// Expression
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
return updateArrayLiteral(<ArrayLiteralExpression>node,
|
||||
visitNodes((<ArrayLiteralExpression>node).elements, visitor, isExpression));
|
||||
nodesVisitor((<ArrayLiteralExpression>node).elements, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return updateObjectLiteral(<ObjectLiteralExpression>node,
|
||||
visitNodes((<ObjectLiteralExpression>node).properties, visitor, isObjectLiteralElementLike));
|
||||
nodesVisitor((<ObjectLiteralExpression>node).properties, visitor, isObjectLiteralElementLike));
|
||||
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
return updatePropertyAccess(<PropertyAccessExpression>node,
|
||||
@@ -336,14 +430,14 @@ namespace ts {
|
||||
case SyntaxKind.CallExpression:
|
||||
return updateCall(<CallExpression>node,
|
||||
visitNode((<CallExpression>node).expression, visitor, isExpression),
|
||||
visitNodes((<CallExpression>node).typeArguments, visitor, isTypeNode),
|
||||
visitNodes((<CallExpression>node).arguments, visitor, isExpression));
|
||||
nodesVisitor((<CallExpression>node).typeArguments, visitor, isTypeNode),
|
||||
nodesVisitor((<CallExpression>node).arguments, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.NewExpression:
|
||||
return updateNew(<NewExpression>node,
|
||||
visitNode((<NewExpression>node).expression, visitor, isExpression),
|
||||
visitNodes((<NewExpression>node).typeArguments, visitor, isTypeNode),
|
||||
visitNodes((<NewExpression>node).arguments, visitor, isExpression));
|
||||
nodesVisitor((<NewExpression>node).typeArguments, visitor, isTypeNode),
|
||||
nodesVisitor((<NewExpression>node).arguments, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return updateTaggedTemplate(<TaggedTemplateExpression>node,
|
||||
@@ -361,19 +455,19 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.FunctionExpression:
|
||||
return updateFunctionExpression(<FunctionExpression>node,
|
||||
visitNodes((<FunctionExpression>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<FunctionExpression>node).modifiers, visitor, isModifier),
|
||||
(<FunctionExpression>node).asteriskToken,
|
||||
visitNode((<FunctionExpression>node).name, visitor, isIdentifier),
|
||||
visitNodes((<FunctionExpression>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<FunctionExpression>node).parameters, visitor, context),
|
||||
nodesVisitor((<FunctionExpression>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<FunctionExpression>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<FunctionExpression>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<FunctionExpression>node).body, visitor, context));
|
||||
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return updateArrowFunction(<ArrowFunction>node,
|
||||
visitNodes((<ArrowFunction>node).modifiers, visitor, isModifier),
|
||||
visitNodes((<ArrowFunction>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<ArrowFunction>node).parameters, visitor, context),
|
||||
nodesVisitor((<ArrowFunction>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ArrowFunction>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<ArrowFunction>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<ArrowFunction>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<ArrowFunction>node).body, visitor, context));
|
||||
|
||||
@@ -415,7 +509,7 @@ namespace ts {
|
||||
case SyntaxKind.TemplateExpression:
|
||||
return updateTemplateExpression(<TemplateExpression>node,
|
||||
visitNode((<TemplateExpression>node).head, visitor, isTemplateHead),
|
||||
visitNodes((<TemplateExpression>node).templateSpans, visitor, isTemplateSpan));
|
||||
nodesVisitor((<TemplateExpression>node).templateSpans, visitor, isTemplateSpan));
|
||||
|
||||
case SyntaxKind.YieldExpression:
|
||||
return updateYield(<YieldExpression>node,
|
||||
@@ -428,15 +522,15 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.ClassExpression:
|
||||
return updateClassExpression(<ClassExpression>node,
|
||||
visitNodes((<ClassExpression>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ClassExpression>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ClassExpression>node).name, visitor, isIdentifier),
|
||||
visitNodes((<ClassExpression>node).typeParameters, visitor, isTypeParameter),
|
||||
visitNodes((<ClassExpression>node).heritageClauses, visitor, isHeritageClause),
|
||||
visitNodes((<ClassExpression>node).members, visitor, isClassElement));
|
||||
nodesVisitor((<ClassExpression>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ClassExpression>node).heritageClauses, visitor, isHeritageClause),
|
||||
nodesVisitor((<ClassExpression>node).members, visitor, isClassElement));
|
||||
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
return updateExpressionWithTypeArguments(<ExpressionWithTypeArguments>node,
|
||||
visitNodes((<ExpressionWithTypeArguments>node).typeArguments, visitor, isTypeNode),
|
||||
nodesVisitor((<ExpressionWithTypeArguments>node).typeArguments, visitor, isTypeNode),
|
||||
visitNode((<ExpressionWithTypeArguments>node).expression, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.AsExpression:
|
||||
@@ -457,11 +551,11 @@ namespace ts {
|
||||
// Element
|
||||
case SyntaxKind.Block:
|
||||
return updateBlock(<Block>node,
|
||||
visitNodes((<Block>node).statements, visitor, isStatement));
|
||||
nodesVisitor((<Block>node).statements, visitor, isStatement));
|
||||
|
||||
case SyntaxKind.VariableStatement:
|
||||
return updateVariableStatement(<VariableStatement>node,
|
||||
visitNodes((<VariableStatement>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<VariableStatement>node).modifiers, visitor, isModifier),
|
||||
visitNode((<VariableStatement>node).declarationList, visitor, isVariableDeclarationList));
|
||||
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
@@ -549,61 +643,61 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.VariableDeclarationList:
|
||||
return updateVariableDeclarationList(<VariableDeclarationList>node,
|
||||
visitNodes((<VariableDeclarationList>node).declarations, visitor, isVariableDeclaration));
|
||||
nodesVisitor((<VariableDeclarationList>node).declarations, visitor, isVariableDeclaration));
|
||||
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
return updateFunctionDeclaration(<FunctionDeclaration>node,
|
||||
visitNodes((<FunctionDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<FunctionDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<FunctionDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<FunctionDeclaration>node).modifiers, visitor, isModifier),
|
||||
(<FunctionDeclaration>node).asteriskToken,
|
||||
visitNode((<FunctionDeclaration>node).name, visitor, isIdentifier),
|
||||
visitNodes((<FunctionDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<FunctionDeclaration>node).parameters, visitor, context),
|
||||
nodesVisitor((<FunctionDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
visitParameterList((<FunctionDeclaration>node).parameters, visitor, context, nodesVisitor),
|
||||
visitNode((<FunctionDeclaration>node).type, visitor, isTypeNode),
|
||||
visitFunctionBody((<FunctionExpression>node).body, visitor, context));
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return updateClassDeclaration(<ClassDeclaration>node,
|
||||
visitNodes((<ClassDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ClassDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ClassDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ClassDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ClassDeclaration>node).name, visitor, isIdentifier),
|
||||
visitNodes((<ClassDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
visitNodes((<ClassDeclaration>node).heritageClauses, visitor, isHeritageClause),
|
||||
visitNodes((<ClassDeclaration>node).members, visitor, isClassElement));
|
||||
nodesVisitor((<ClassDeclaration>node).typeParameters, visitor, isTypeParameter),
|
||||
nodesVisitor((<ClassDeclaration>node).heritageClauses, visitor, isHeritageClause),
|
||||
nodesVisitor((<ClassDeclaration>node).members, visitor, isClassElement));
|
||||
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return updateEnumDeclaration(<EnumDeclaration>node,
|
||||
visitNodes((<EnumDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<EnumDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<EnumDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<EnumDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<EnumDeclaration>node).name, visitor, isIdentifier),
|
||||
visitNodes((<EnumDeclaration>node).members, visitor, isEnumMember));
|
||||
nodesVisitor((<EnumDeclaration>node).members, visitor, isEnumMember));
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return updateModuleDeclaration(<ModuleDeclaration>node,
|
||||
visitNodes((<ModuleDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ModuleDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ModuleDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ModuleDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ModuleDeclaration>node).name, visitor, isIdentifier),
|
||||
visitNode((<ModuleDeclaration>node).body, visitor, isModuleBody));
|
||||
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return updateModuleBlock(<ModuleBlock>node,
|
||||
visitNodes((<ModuleBlock>node).statements, visitor, isStatement));
|
||||
nodesVisitor((<ModuleBlock>node).statements, visitor, isStatement));
|
||||
|
||||
case SyntaxKind.CaseBlock:
|
||||
return updateCaseBlock(<CaseBlock>node,
|
||||
visitNodes((<CaseBlock>node).clauses, visitor, isCaseOrDefaultClause));
|
||||
nodesVisitor((<CaseBlock>node).clauses, visitor, isCaseOrDefaultClause));
|
||||
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return updateImportEqualsDeclaration(<ImportEqualsDeclaration>node,
|
||||
visitNodes((<ImportEqualsDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ImportEqualsDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ImportEqualsDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ImportEqualsDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ImportEqualsDeclaration>node).name, visitor, isIdentifier),
|
||||
visitNode((<ImportEqualsDeclaration>node).moduleReference, visitor, isModuleReference));
|
||||
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return updateImportDeclaration(<ImportDeclaration>node,
|
||||
visitNodes((<ImportDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ImportDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ImportDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ImportDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ImportDeclaration>node).importClause, visitor, isImportClause),
|
||||
visitNode((<ImportDeclaration>node).moduleSpecifier, visitor, isExpression));
|
||||
|
||||
@@ -618,7 +712,7 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.NamedImports:
|
||||
return updateNamedImports(<NamedImports>node,
|
||||
visitNodes((<NamedImports>node).elements, visitor, isImportSpecifier));
|
||||
nodesVisitor((<NamedImports>node).elements, visitor, isImportSpecifier));
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
return updateImportSpecifier(<ImportSpecifier>node,
|
||||
@@ -627,20 +721,20 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.ExportAssignment:
|
||||
return updateExportAssignment(<ExportAssignment>node,
|
||||
visitNodes((<ExportAssignment>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ExportAssignment>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ExportAssignment>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ExportAssignment>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ExportAssignment>node).expression, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return updateExportDeclaration(<ExportDeclaration>node,
|
||||
visitNodes((<ExportDeclaration>node).decorators, visitor, isDecorator),
|
||||
visitNodes((<ExportDeclaration>node).modifiers, visitor, isModifier),
|
||||
nodesVisitor((<ExportDeclaration>node).decorators, visitor, isDecorator),
|
||||
nodesVisitor((<ExportDeclaration>node).modifiers, visitor, isModifier),
|
||||
visitNode((<ExportDeclaration>node).exportClause, visitor, isNamedExports),
|
||||
visitNode((<ExportDeclaration>node).moduleSpecifier, visitor, isExpression));
|
||||
|
||||
case SyntaxKind.NamedExports:
|
||||
return updateNamedExports(<NamedExports>node,
|
||||
visitNodes((<NamedExports>node).elements, visitor, isExportSpecifier));
|
||||
nodesVisitor((<NamedExports>node).elements, visitor, isExportSpecifier));
|
||||
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
return updateExportSpecifier(<ExportSpecifier>node,
|
||||
@@ -656,12 +750,12 @@ namespace ts {
|
||||
case SyntaxKind.JsxElement:
|
||||
return updateJsxElement(<JsxElement>node,
|
||||
visitNode((<JsxElement>node).openingElement, visitor, isJsxOpeningElement),
|
||||
visitNodes((<JsxElement>node).children, visitor, isJsxChild),
|
||||
nodesVisitor((<JsxElement>node).children, visitor, isJsxChild),
|
||||
visitNode((<JsxElement>node).closingElement, visitor, isJsxClosingElement));
|
||||
|
||||
case SyntaxKind.JsxAttributes:
|
||||
return updateJsxAttributes(<JsxAttributes>node,
|
||||
visitNodes((<JsxAttributes>node).properties, visitor, isJsxAttributeLike));
|
||||
nodesVisitor((<JsxAttributes>node).properties, visitor, isJsxAttributeLike));
|
||||
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
return updateJsxSelfClosingElement(<JsxSelfClosingElement>node,
|
||||
@@ -694,15 +788,15 @@ namespace ts {
|
||||
case SyntaxKind.CaseClause:
|
||||
return updateCaseClause(<CaseClause>node,
|
||||
visitNode((<CaseClause>node).expression, visitor, isExpression),
|
||||
visitNodes((<CaseClause>node).statements, visitor, isStatement));
|
||||
nodesVisitor((<CaseClause>node).statements, visitor, isStatement));
|
||||
|
||||
case SyntaxKind.DefaultClause:
|
||||
return updateDefaultClause(<DefaultClause>node,
|
||||
visitNodes((<DefaultClause>node).statements, visitor, isStatement));
|
||||
nodesVisitor((<DefaultClause>node).statements, visitor, isStatement));
|
||||
|
||||
case SyntaxKind.HeritageClause:
|
||||
return updateHeritageClause(<HeritageClause>node,
|
||||
visitNodes((<HeritageClause>node).types, visitor, isExpressionWithTypeArguments));
|
||||
nodesVisitor((<HeritageClause>node).types, visitor, isExpressionWithTypeArguments));
|
||||
|
||||
case SyntaxKind.CatchClause:
|
||||
return updateCatchClause(<CatchClause>node,
|
||||
@@ -741,7 +835,8 @@ namespace ts {
|
||||
visitNode((<PartiallyEmittedExpression>node).expression, visitor, isExpression));
|
||||
|
||||
default:
|
||||
return node;
|
||||
throw new Error("not handled");
|
||||
// return node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
namespace FourSlash {
|
||||
ts.disableIncrementalParsing = false;
|
||||
|
||||
function normalizeNewLines(s: string) {
|
||||
return s.replace(/\r\n/g, "\n");
|
||||
}
|
||||
|
||||
// Represents a parsed source file with metadata
|
||||
export interface FourSlashFile {
|
||||
// The contents of the file (with markers, etc stripped out)
|
||||
@@ -1958,8 +1962,7 @@ namespace FourSlash {
|
||||
|
||||
public verifyCurrentFileContent(text: string) {
|
||||
const actual = this.getFileContent(this.activeFile.fileName);
|
||||
const replaceNewlines = (str: string) => str.replace(/\r\n/g, "\n");
|
||||
if (replaceNewlines(actual) !== replaceNewlines(text)) {
|
||||
if (normalizeNewLines(actual) !== normalizeNewLines(text)) {
|
||||
throw new Error("verifyCurrentFileContent\n" +
|
||||
"\tExpected: \"" + text + "\"\n" +
|
||||
"\t Actual: \"" + actual + "\"");
|
||||
@@ -2135,7 +2138,7 @@ namespace FourSlash {
|
||||
const actualText = this.rangeText(ranges[0]);
|
||||
|
||||
const result = includeWhiteSpace
|
||||
? actualText === expectedText
|
||||
? normalizeNewLines(actualText) === normalizeNewLines(expectedText)
|
||||
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText);
|
||||
|
||||
if (!result) {
|
||||
@@ -2185,7 +2188,7 @@ namespace FourSlash {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code]);
|
||||
const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code], this.formatCodeSettings);
|
||||
if (newActions && newActions.length) {
|
||||
actions = actions ? actions.concat(newActions) : newActions;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
"./unittests/projectErrors.ts",
|
||||
"./unittests/printer.ts",
|
||||
"./unittests/transform.ts",
|
||||
"./unittests/customTransforms.ts"
|
||||
"./unittests/customTransforms.ts",
|
||||
"./unittests/textChanges.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,520 @@
|
||||
/// <reference path="..\..\compiler\emitter.ts" />
|
||||
/// <reference path="..\..\services\textChanges.ts" />
|
||||
/// <reference path="..\harness.ts" />
|
||||
|
||||
namespace ts {
|
||||
describe("textChanges", () => {
|
||||
function findChild(name: string, n: Node) {
|
||||
return find(n);
|
||||
|
||||
function find(node: Node): Node {
|
||||
if (isDeclaration(node) && node.name && isIdentifier(node.name) && node.name.text === name) {
|
||||
return node;
|
||||
}
|
||||
else {
|
||||
return forEachChild(node, find);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const printerOptions = { newLine: NewLineKind.LineFeed };
|
||||
const newLineCharacter = getNewLineCharacter(printerOptions);
|
||||
|
||||
function getRuleProvider(action?: (opts: FormatCodeSettings) => void) {
|
||||
const options = {
|
||||
indentSize: 4,
|
||||
tabSize: 4,
|
||||
newLineCharacter,
|
||||
convertTabsToSpaces: true,
|
||||
indentStyle: ts.IndentStyle.Smart,
|
||||
insertSpaceAfterConstructor: false,
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
insertSpaceAfterSemicolonInForStatements: true,
|
||||
insertSpaceBeforeAndAfterBinaryOperators: true,
|
||||
insertSpaceAfterKeywordsInControlFlowStatements: true,
|
||||
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
||||
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
|
||||
insertSpaceBeforeFunctionParenthesis: false,
|
||||
placeOpenBraceOnNewLineForFunctions: false,
|
||||
placeOpenBraceOnNewLineForControlBlocks: false,
|
||||
};
|
||||
if (action) {
|
||||
action(options);
|
||||
}
|
||||
const rulesProvider = new formatting.RulesProvider();
|
||||
rulesProvider.ensureUpToDate(options);
|
||||
return rulesProvider;
|
||||
}
|
||||
|
||||
// validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed.
|
||||
function verifyPositions({ text, node }: textChanges.NonFormattedText): void {
|
||||
const nodeList = flattenNodes(node);
|
||||
const sourceFile = createSourceFile("f.ts", text, ScriptTarget.ES2015);
|
||||
const parsedNodeList = flattenNodes(sourceFile.statements[0]);
|
||||
Debug.assert(nodeList.length === parsedNodeList.length);
|
||||
for (let i = 0; i < nodeList.length; i++) {
|
||||
const left = nodeList[i];
|
||||
const right = parsedNodeList[i];
|
||||
Debug.assert(left.pos === right.pos);
|
||||
Debug.assert(left.end === right.end);
|
||||
}
|
||||
|
||||
function flattenNodes(n: Node) {
|
||||
const data: (Node | NodeArray<any>)[] = [];
|
||||
walk(n);
|
||||
return data;
|
||||
|
||||
function walk(n: Node | Node[]): void {
|
||||
data.push(<any>n);
|
||||
return isArray(n) ? forEach(n, walk) : forEachChild(n, walk, walk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runSingleFileTest(caption: string, setupFormatOptions: (opts: FormatCodeSettings) => void, text: string, validateNodes: boolean, testBlock: (sourceFile: SourceFile, changeTracker: textChanges.ChangeTracker) => void) {
|
||||
it(caption, () => {
|
||||
Harness.Baseline.runBaseline(`textChanges/${caption}.js`, () => {
|
||||
const sourceFile = createSourceFile("source.ts", text, ScriptTarget.ES2015, /*setParentNodes*/ true);
|
||||
const rulesProvider = getRuleProvider(setupFormatOptions);
|
||||
const changeTracker = new textChanges.ChangeTracker(printerOptions.newLine, rulesProvider, validateNodes ? verifyPositions : undefined);
|
||||
testBlock(sourceFile, changeTracker);
|
||||
const changes = changeTracker.getChanges();
|
||||
assert.equal(changes.length, 1);
|
||||
assert.equal(changes[0].fileName, sourceFile.fileName);
|
||||
const modified = textChanges.applyChanges(sourceFile.text, changes[0].textChanges);
|
||||
return `===ORIGINAL===${newLineCharacter}${text}${newLineCharacter}===MODIFIED===${newLineCharacter}${modified}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setNewLineForOpenBraceInFunctions(opts: FormatCodeSettings) {
|
||||
opts.placeOpenBraceOnNewLineForFunctions = true;
|
||||
}
|
||||
|
||||
{
|
||||
const text = `
|
||||
namespace M
|
||||
{
|
||||
namespace M2
|
||||
{
|
||||
function foo() {
|
||||
// comment 1
|
||||
const x = 1;
|
||||
|
||||
/**
|
||||
* comment 2 line 1
|
||||
* comment 2 line 2
|
||||
*/
|
||||
function f() {
|
||||
return 100;
|
||||
}
|
||||
const y = 2; // comment 3
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}`;
|
||||
runSingleFileTest("extractMethodLike", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
const statements = (<Block>(<FunctionDeclaration>findChild("foo", sourceFile)).body).statements.slice(1);
|
||||
const newFunction = createFunctionDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ "bar",
|
||||
/*typeParameters*/ undefined,
|
||||
/*parameters*/ emptyArray,
|
||||
/*type*/ createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
||||
/*body */ createBlock(statements)
|
||||
);
|
||||
|
||||
changeTracker.insertNodeBefore(sourceFile, /*before*/findChild("M2", sourceFile), newFunction, { insertTrailingNewLine: true });
|
||||
|
||||
// replace statements with return statement
|
||||
const newStatement = createReturn(
|
||||
createCall(
|
||||
/*expression*/ newFunction.name,
|
||||
/*typeArguments*/ undefined,
|
||||
/*argumentsArray*/ emptyArray
|
||||
));
|
||||
changeTracker.replaceNodeRange(sourceFile, statements[0], lastOrUndefined(statements), newStatement, { insertTrailingNewLine: true });
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
function foo() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function bar() {
|
||||
return 2;
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("deleteRange1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteRange(sourceFile, { pos: text.indexOf("function foo"), end: text.indexOf("function bar") });
|
||||
});
|
||||
}
|
||||
function findVariableStatementContaining(name: string, sourceFile: SourceFile) {
|
||||
const varDecl = findChild(name, sourceFile);
|
||||
assert.equal(varDecl.kind, SyntaxKind.VariableDeclaration);
|
||||
const varStatement = varDecl.parent.parent;
|
||||
assert.equal(varStatement.kind, SyntaxKind.VariableStatement);
|
||||
return varStatement;
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
var x = 1; // some comment - 1
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
`;
|
||||
runSingleFileTest("deleteNode1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNode(sourceFile, findVariableStatementContaining("y", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNode2", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNode(sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedStartPosition: true });
|
||||
});
|
||||
runSingleFileTest("deleteNode3", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNode(sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedEndPosition: true });
|
||||
});
|
||||
runSingleFileTest("deleteNode4", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNode(sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
||||
});
|
||||
runSingleFileTest("deleteNode5", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNode(sourceFile, findVariableStatementContaining("x", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
`;
|
||||
runSingleFileTest("deleteNodeRange1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeRange2", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
||||
{ useNonAdjustedStartPosition: true });
|
||||
});
|
||||
runSingleFileTest("deleteNodeRange3", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
||||
{ useNonAdjustedEndPosition: true });
|
||||
});
|
||||
runSingleFileTest("deleteNodeRange4", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
||||
{ useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
||||
});
|
||||
}
|
||||
function createTestVariableDeclaration(name: string) {
|
||||
return createVariableDeclaration(name, /*type*/ undefined, createObjectLiteral([createPropertyAssignment("p1", createLiteral(1))], /*multiline*/ true));
|
||||
}
|
||||
function createTestClass() {
|
||||
return createClassDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
[
|
||||
createToken(SyntaxKind.PublicKeyword)
|
||||
],
|
||||
"class1",
|
||||
/*typeParameters*/ undefined,
|
||||
[
|
||||
createHeritageClause(
|
||||
SyntaxKind.ImplementsKeyword,
|
||||
[
|
||||
createExpressionWithTypeArguments(/*typeArguments*/ undefined, createIdentifier("interface1"))
|
||||
]
|
||||
)
|
||||
],
|
||||
[
|
||||
createProperty(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
"property1",
|
||||
/*questionToken*/ undefined,
|
||||
createKeywordTypeNode(SyntaxKind.BooleanKeyword),
|
||||
/*initializer*/ undefined
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7`;
|
||||
runSingleFileTest("replaceRange", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceRange(sourceFile, { pos: text.indexOf("var y"), end: text.indexOf("var a") }, createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceRangeWithForcedIndentation", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceRange(sourceFile, { pos: text.indexOf("var y"), end: text.indexOf("var a") }, createTestClass(), { insertTrailingNewLine: true, indentation: 8, delta: 0 });
|
||||
});
|
||||
|
||||
runSingleFileTest("replaceRangeNoLineBreakBefore", setNewLineForOpenBraceInFunctions, `const x = 1, y = "2";`, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
const newNode = createTestVariableDeclaration("z1");
|
||||
changeTracker.replaceRange(sourceFile, { pos: sourceFile.text.indexOf("y"), end: sourceFile.text.indexOf(";") }, newNode);
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
namespace A {
|
||||
const x = 1, y = "2";
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("replaceNode1NoLineBreakBefore", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
const newNode = createTestVariableDeclaration("z1");
|
||||
changeTracker.replaceNode(sourceFile, findChild("y", sourceFile), newNode);
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7`;
|
||||
runSingleFileTest("replaceNode1", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceNode2", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, insertTrailingNewLine: true, insertLeadingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceNode3", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedEndPosition: true, insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceNode4", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
||||
});
|
||||
runSingleFileTest("replaceNode5", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7`;
|
||||
runSingleFileTest("replaceNodeRange1", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceNodeRange2", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, insertTrailingNewLine: true, insertLeadingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceNodeRange3", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedEndPosition: true, insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("replaceNodeRange4", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7`;
|
||||
runSingleFileTest("insertNodeAt1", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAt(sourceFile, text.indexOf("var y"), createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("insertNodeAt2", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAt(sourceFile, text.indexOf("; // comment 4"), createTestVariableDeclaration("z1"));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}`;
|
||||
runSingleFileTest("insertNodeBefore1", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("insertNodeBefore2", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeBefore(sourceFile, findChild("M", sourceFile), createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("insertNodeAfter1", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { insertTrailingNewLine: true });
|
||||
});
|
||||
runSingleFileTest("insertNodeAfter2", setNewLineForOpenBraceInFunctions, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass(), { insertLeadingNewLine: true });
|
||||
});
|
||||
}
|
||||
{
|
||||
function findOpenBraceForConstructor(sourceFile: SourceFile) {
|
||||
const classDecl = <ClassDeclaration>sourceFile.statements[0];
|
||||
const constructorDecl = forEach(classDecl.members, m => m.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>m).body && <ConstructorDeclaration>m);
|
||||
return constructorDecl.body.getFirstToken();
|
||||
}
|
||||
function createTestSuperCall() {
|
||||
const superCall = createCall(
|
||||
createSuper(),
|
||||
/*typeArguments*/ undefined,
|
||||
/*argumentsArray*/ emptyArray
|
||||
);
|
||||
return createStatement(superCall);
|
||||
}
|
||||
const text1 = `
|
||||
class A {
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfter3", noop, text1, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findOpenBraceForConstructor(sourceFile), createTestSuperCall(), { insertTrailingNewLine: true });
|
||||
});
|
||||
const text2 = `
|
||||
class A {
|
||||
constructor() {
|
||||
var x = 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfter4", noop, text2, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), createTestSuperCall(), { insertTrailingNewLine: true });
|
||||
});
|
||||
const text3 = `
|
||||
class A {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfter3-block with newline", noop, text3, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findOpenBraceForConstructor(sourceFile), createTestSuperCall(), { insertTrailingNewLine: true });
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `var a = 1, b = 2, c = 3;`;
|
||||
runSingleFileTest("deleteNodeInList1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList2", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList3", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `var a = 1,b = 2,c = 3;`;
|
||||
runSingleFileTest("deleteNodeInList1_1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList2_1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList3_1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
namespace M {
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
}`;
|
||||
runSingleFileTest("deleteNodeInList4", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList5", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList6", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
namespace M {
|
||||
var a = 1, // comment 1
|
||||
// comment 2
|
||||
b = 2, // comment 3
|
||||
// comment 4
|
||||
c = 3; // comment 5
|
||||
}`;
|
||||
runSingleFileTest("deleteNodeInList4_1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList5_1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList6_1", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
function foo(a: number, b: string, c = true) {
|
||||
return 1;
|
||||
}`;
|
||||
runSingleFileTest("deleteNodeInList7", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList8", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList9", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
function foo(a: number,b: string,c = true) {
|
||||
return 1;
|
||||
}`;
|
||||
runSingleFileTest("deleteNodeInList10", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList11", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList12", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
function foo(
|
||||
a: number,
|
||||
b: string,
|
||||
c = true) {
|
||||
return 1;
|
||||
}`;
|
||||
runSingleFileTest("deleteNodeInList13", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("a", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList14", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("b", sourceFile));
|
||||
});
|
||||
runSingleFileTest("deleteNodeInList15", noop, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.deleteNodeInList(sourceFile, findChild("c", sourceFile));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1420,8 +1420,9 @@ namespace ts.server {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
const startPosition = getStartPosition();
|
||||
const endPosition = getEndPosition();
|
||||
const formatOptions = this.projectService.getFormatCodeOptions(file);
|
||||
|
||||
const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes);
|
||||
const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, formatOptions);
|
||||
if (!codeActions) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace ts {
|
||||
newLineCharacter: string;
|
||||
host: LanguageServiceHost;
|
||||
cancellationToken: CancellationToken;
|
||||
rulesProvider: formatting.RulesProvider;
|
||||
}
|
||||
|
||||
export namespace codefix {
|
||||
|
||||
@@ -31,37 +31,49 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let typeString = "any";
|
||||
let typeNode: TypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword);
|
||||
|
||||
if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = token.parent.parent as BinaryExpression;
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right)));
|
||||
typeString = checker.typeToString(widenedType);
|
||||
typeNode = checker.createTypeNode(widenedType) || typeNode;
|
||||
}
|
||||
|
||||
const startPos = classDeclaration.members.pos;
|
||||
const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile);
|
||||
|
||||
const property = createProperty(
|
||||
/*decorators*/undefined
|
||||
, /*modifiers*/ undefined
|
||||
, token.getText(sourceFile)
|
||||
, /*questionToken*/ undefined
|
||||
, typeNode
|
||||
, /*initializer*/ undefined);
|
||||
// TODO: make index signature.
|
||||
const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { insertTrailingNewLine: true });
|
||||
|
||||
const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword);
|
||||
const indexingParameter = createParameter(
|
||||
/*decorators*/ undefined
|
||||
, /*modifiers*/ undefined
|
||||
, /*dotDotDotToken*/ undefined
|
||||
, "x"
|
||||
, /*questionToken*/ undefined
|
||||
, stringTypeNode);
|
||||
const indexSignature = createIndexSignatureDeclaration([indexingParameter], typeNode);
|
||||
|
||||
const indexSignatureChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
indexSignatureChangeTracker.insertNodeAfter(sourceFile, openBrace, indexSignature, { insertTrailingNewLine: true });
|
||||
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: `${token.getFullText(sourceFile)}: ${typeString};`
|
||||
}]
|
||||
}]
|
||||
changes: propertyChangeTracker.getChanges()
|
||||
},
|
||||
{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_missing_property_0), [token.getText()]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: `[name: string]: ${typeString};`
|
||||
}]
|
||||
}]
|
||||
changes: indexSignatureChangeTracker.getChanges()
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,9 @@ namespace ts.codefix {
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
if (isClassLike(token.parent)) {
|
||||
const classDecl = token.parent as ClassLikeDeclaration;
|
||||
const startPos = classDecl.members.pos;
|
||||
const classDeclaration = token.parent as ClassLikeDeclaration;
|
||||
|
||||
const extendsNode = getClassExtendsHeritageClauseElement(classDecl);
|
||||
const extendsNode = getClassExtendsHeritageClauseElement(classDeclaration);
|
||||
const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode);
|
||||
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
@@ -30,18 +29,12 @@ namespace ts.codefix {
|
||||
const extendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType);
|
||||
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
|
||||
|
||||
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
|
||||
|
||||
if (insertion.length) {
|
||||
const newNodes = createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker);
|
||||
const changes = newNodesToChanges(newNodes, getOpenBraceOfClassLike(classDeclaration, sourceFile), context);
|
||||
if(changes && changes.length > 0) {
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: insertion
|
||||
}]
|
||||
}]
|
||||
changes
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const startPos: number = classDecl.members.pos;
|
||||
const openBrace = getOpenBraceOfClassLike(classDecl, sourceFile);
|
||||
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
|
||||
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
|
||||
|
||||
@@ -31,43 +31,45 @@ namespace ts.codefix {
|
||||
const implementedTypeSymbols = checker.getPropertiesOfType(implementedType);
|
||||
const nonPrivateMembers = implementedTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private));
|
||||
|
||||
let insertion = getMissingIndexSignatureInsertion(implementedType, IndexKind.Number, classDecl, hasNumericIndexSignature);
|
||||
insertion += getMissingIndexSignatureInsertion(implementedType, IndexKind.String, classDecl, hasStringIndexSignature);
|
||||
insertion += getMissingMembersInsertion(classDecl, nonPrivateMembers, checker, context.newLineCharacter);
|
||||
|
||||
let newNodes: Node[] = [];
|
||||
createAndAddMissingIndexSignatureDeclaration(implementedType, IndexKind.Number, hasNumericIndexSignature, newNodes);
|
||||
createAndAddMissingIndexSignatureDeclaration(implementedType, IndexKind.String, hasStringIndexSignature, newNodes);
|
||||
newNodes = newNodes.concat(createMissingMemberNodes(classDecl, nonPrivateMembers, checker));
|
||||
const message = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]);
|
||||
if (insertion) {
|
||||
pushAction(result, insertion, message);
|
||||
if (newNodes.length > 0) {
|
||||
pushAction(result, newNodes, message);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
function getMissingIndexSignatureInsertion(type: InterfaceType, kind: IndexKind, enclosingDeclaration: ClassLikeDeclaration, hasIndexSigOfKind: boolean) {
|
||||
if (!hasIndexSigOfKind) {
|
||||
const IndexInfoOfKind = checker.getIndexInfoOfType(type, kind);
|
||||
if (IndexInfoOfKind) {
|
||||
const writer = getSingleLineStringWriter();
|
||||
checker.getSymbolDisplayBuilder().buildIndexSignatureDisplay(IndexInfoOfKind, writer, kind, enclosingDeclaration);
|
||||
const result = writer.string();
|
||||
releaseStringWriter(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
function createAndAddMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind, hasIndexSigOfKind: boolean, newNodes: Node[]): void {
|
||||
if (hasIndexSigOfKind) {
|
||||
return undefined;
|
||||
}
|
||||
return "";
|
||||
|
||||
const indexInfoOfKind = checker.getIndexInfoOfType(type, kind);
|
||||
|
||||
if (!indexInfoOfKind) {
|
||||
return undefined;
|
||||
}
|
||||
const typeNode = checker.createTypeNode(indexInfoOfKind.type);
|
||||
const newIndexSignatureDeclaration = createIndexSignatureDeclaration(
|
||||
[createParameter(
|
||||
/*decorators*/undefined
|
||||
, /*modifiers*/ undefined
|
||||
, /*dotDotDotToken*/ undefined
|
||||
, getNameFromIndexInfo(indexInfoOfKind)
|
||||
, /*questionToken*/ undefined
|
||||
, kind === IndexKind.String ? createKeywordTypeNode(SyntaxKind.StringKeyword) : createKeywordTypeNode(SyntaxKind.NumberKeyword))]
|
||||
, typeNode);
|
||||
newNodes.push(newIndexSignatureDeclaration);
|
||||
}
|
||||
|
||||
function pushAction(result: CodeAction[], insertion: string, description: string): void {
|
||||
function pushAction(result: CodeAction[], newNodes: Node[], description: string): void {
|
||||
const newAction: CodeAction = {
|
||||
description: description,
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: insertion
|
||||
}]
|
||||
}]
|
||||
changes: newNodesToChanges(newNodes, openBrace, context)
|
||||
};
|
||||
result.push(newAction);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// figure out if the this access is actuall inside the supercall
|
||||
// figure out if the `this` access is actually inside the supercall
|
||||
// i.e. super(this.a), since in that case we won't suggest a fix
|
||||
if (superCall.expression && superCall.expression.kind == SyntaxKind.CallExpression) {
|
||||
const arguments = (<CallExpression>superCall.expression).arguments;
|
||||
@@ -26,22 +26,13 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>constructor, sourceFile);
|
||||
const changes = [{
|
||||
fileName: sourceFile.fileName, textChanges: [{
|
||||
newText: superCall.getText(sourceFile),
|
||||
span: { start: newPosition, length: 0 }
|
||||
},
|
||||
{
|
||||
newText: "",
|
||||
span: { start: superCall.getStart(sourceFile), length: superCall.getWidth(sourceFile) }
|
||||
}]
|
||||
}];
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
changeTracker.insertNodeAfter(sourceFile, getOpenBrace(<ConstructorDeclaration>constructor, sourceFile), superCall, { insertTrailingNewLine: true });
|
||||
changeTracker.deleteNode(sourceFile, superCall);
|
||||
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor),
|
||||
changes
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
|
||||
function findSuperCall(n: Node): ExpressionStatement {
|
||||
|
||||
@@ -10,10 +10,13 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const newPosition = getOpenBraceEnd(<ConstructorDeclaration>token.parent, sourceFile);
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
const superCall = createStatement(createCall(createSuper(), /*typeArguments*/ undefined, /*argumentsArray*/ emptyArray));
|
||||
changeTracker.insertNodeAfter(sourceFile, getOpenBrace(<ConstructorDeclaration>token.parent, sourceFile), superCall, { insertTrailingNewLine: true });
|
||||
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call),
|
||||
changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "super();", span: { start: newPosition, length: 0 } }] }]
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,26 +21,20 @@ namespace ts.codefix {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let changeStart = extendsToken.getStart(sourceFile);
|
||||
let changeEnd = extendsToken.getEnd();
|
||||
const textChanges: TextChange[] = [{ newText: " implements", span: { start: changeStart, length: changeEnd - changeStart } }];
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
changeTracker.replaceNode(sourceFile, extendsToken, createToken(SyntaxKind.ImplementsKeyword));
|
||||
|
||||
// We replace existing keywords with commas.
|
||||
for (let i = 1; i < heritageClauses.length; i++) {
|
||||
const keywordToken = heritageClauses[i].getFirstToken();
|
||||
if (keywordToken) {
|
||||
changeStart = keywordToken.getStart(sourceFile);
|
||||
changeEnd = keywordToken.getEnd();
|
||||
textChanges.push({ newText: ",", span: { start: changeStart, length: changeEnd - changeStart } });
|
||||
changeTracker.replaceNode(sourceFile, keywordToken, createToken(SyntaxKind.CommaToken));
|
||||
}
|
||||
}
|
||||
|
||||
const result = [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: textChanges
|
||||
}]
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
|
||||
return result;
|
||||
|
||||
@@ -5,11 +5,15 @@ namespace ts.codefix {
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
const start = token.getStart(sourceFile);
|
||||
if (token.kind !== SyntaxKind.Identifier) {
|
||||
return undefined;
|
||||
}
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
changeTracker.replaceNode(sourceFile, token, createPropertyAccess(createThis(), <Identifier>token));
|
||||
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Add_this_to_unresolved_variable),
|
||||
changes: [{ fileName: sourceFile.fileName, textChanges: [{ newText: "this.", span: { start, length: 0 } }] }]
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,37 +1,60 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
|
||||
export function newNodesToChanges(newNodes: Node[], insertAfter: Node, context: CodeFixContext) {
|
||||
const sourceFile = context.sourceFile;
|
||||
if (!(newNodes)) {
|
||||
// TODO: make the appropriate value flow through gracefully.
|
||||
throw new Error("newNodesToChanges expects an array");
|
||||
}
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
|
||||
for (const newNode of newNodes) {
|
||||
changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode, { insertTrailingNewLine: true });
|
||||
}
|
||||
return changeTracker.getChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds members of the resolved type that are missing in the class pointed to by class decl
|
||||
* and generates source code for the missing members.
|
||||
* @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for.
|
||||
* @returns Empty string iff there are no member insertions.
|
||||
*/
|
||||
export function getMissingMembersInsertion(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker, newlineChar: string): string {
|
||||
export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker): Node[] {
|
||||
const classMembers = classDeclaration.symbol.members;
|
||||
const missingMembers = possiblyMissingSymbols.filter(symbol => !classMembers.has(symbol.getName()));
|
||||
|
||||
let insertion = "";
|
||||
|
||||
let newNodes: Node[] = [];
|
||||
for (const symbol of missingMembers) {
|
||||
insertion = insertion.concat(getInsertionForMemberSymbol(symbol, classDeclaration, checker, newlineChar));
|
||||
const newNode = createNewNodeForMemberSymbol(symbol, classDeclaration, checker);
|
||||
if (newNode) {
|
||||
if (Array.isArray(newNode)) {
|
||||
newNodes = newNodes.concat(newNode);
|
||||
}
|
||||
else {
|
||||
newNodes.push(newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return insertion;
|
||||
return newNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`.
|
||||
*/
|
||||
function getInsertionForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, newlineChar: string): string {
|
||||
function createNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker): Node[] | Node | undefined {
|
||||
const declarations = symbol.getDeclarations();
|
||||
if (!(declarations && declarations.length)) {
|
||||
return "";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const declaration = declarations[0] as Declaration;
|
||||
// TODO: get name as identifier or computer property name, etc.
|
||||
const name = declaration.name ? declaration.name.getText() : undefined;
|
||||
const visibility = getVisibilityPrefixWithSpace(getModifierFlags(declaration));
|
||||
|
||||
const visibilityModifier = createVisibilityModifier(getModifierFlags(declaration));
|
||||
const modifiers = visibilityModifier ? [visibilityModifier] : undefined;
|
||||
const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration);
|
||||
|
||||
switch (declaration.kind) {
|
||||
@@ -39,9 +62,16 @@ namespace ts.codefix {
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
const typeString = checker.typeToString(type, enclosingDeclaration, TypeFormatFlags.None);
|
||||
return `${visibility}${name}: ${typeString};${newlineChar}`;
|
||||
|
||||
const typeNode = checker.createTypeNode(type);
|
||||
// TODO: add modifiers.
|
||||
const property = createProperty(
|
||||
/*decorators*/undefined
|
||||
, modifiers
|
||||
, name
|
||||
, /*questionToken*/ undefined
|
||||
, typeNode
|
||||
, /*initializer*/ undefined);
|
||||
return property;
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// The signature for the implementation appears as an entry in `signatures` iff
|
||||
@@ -53,43 +83,59 @@ namespace ts.codefix {
|
||||
// correspondence of declarations and signatures.
|
||||
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
|
||||
if (!(signatures && signatures.length > 0)) {
|
||||
return "";
|
||||
return undefined;
|
||||
}
|
||||
if (declarations.length === 1) {
|
||||
Debug.assert(signatures.length === 1);
|
||||
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
return getStubbedMethod(visibility, name, sigString, newlineChar);
|
||||
// TODO: suppress any return type
|
||||
// TODO: get parameters working.
|
||||
// TODO: add support for type parameters.
|
||||
const signature = signatures[0];
|
||||
const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker));
|
||||
const returnType = checker.createTypeNode(signature.resolvedReturnType);
|
||||
return createStubbedMethod(modifiers, name, /*typeParameters*/undefined, newParameterNodes, returnType);
|
||||
}
|
||||
|
||||
let result = "";
|
||||
let signatureDeclarations = [];
|
||||
for (let i = 0; i < signatures.length; i++) {
|
||||
const sigString = checker.signatureToString(signatures[i], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += `${visibility}${name}${sigString};${newlineChar}`;
|
||||
// const sigString = checker.signatureToString(signatures[i], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
// TODO: make signatures instead of methods
|
||||
const signature = signatures[i];
|
||||
const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker));
|
||||
const returnType = checker.createTypeNode(signature.resolvedReturnType);
|
||||
signatureDeclarations.push(createMethod(
|
||||
/*decorators*/ undefined
|
||||
, modifiers
|
||||
, /*asteriskToken*/ undefined
|
||||
, name
|
||||
, /*typeParameters*/undefined
|
||||
, newParameterNodes
|
||||
, returnType
|
||||
, /*body*/undefined));
|
||||
}
|
||||
|
||||
// If there is a declaration with a body, it is the last declaration,
|
||||
// and it isn't caught by `getSignaturesOfType`.
|
||||
let bodySig: Signature | undefined = undefined;
|
||||
if (declarations.length > signatures.length) {
|
||||
bodySig = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration);
|
||||
let signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration);
|
||||
const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker));
|
||||
const returnType = checker.createTypeNode(signature.resolvedReturnType);
|
||||
signatureDeclarations.push(createStubbedMethod(modifiers, name, /*typeParameters*/undefined, newParameterNodes, returnType));
|
||||
}
|
||||
else {
|
||||
Debug.assert(declarations.length === signatures.length);
|
||||
bodySig = createBodySignatureWithAnyTypes(signatures, enclosingDeclaration, checker);
|
||||
const methodImplementingSignatures = createMethodImplementingSignatures(signatures, enclosingDeclaration, name, modifiers);
|
||||
signatureDeclarations.push(methodImplementingSignatures);
|
||||
}
|
||||
const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += getStubbedMethod(visibility, name, sigString, newlineChar);
|
||||
|
||||
return result;
|
||||
return signatureDeclarations;
|
||||
default:
|
||||
return "";
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function createBodySignatureWithAnyTypes(signatures: Signature[], enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker): Signature {
|
||||
const newSignatureDeclaration = createNode(SyntaxKind.CallSignature) as SignatureDeclaration;
|
||||
newSignatureDeclaration.parent = enclosingDeclaration;
|
||||
newSignatureDeclaration.name = signatures[0].getDeclaration().name;
|
||||
// TODO: infer types of arguments?
|
||||
function createMethodImplementingSignatures(signatures: Signature[], enclosingDeclaration: ClassLikeDeclaration, name: string, modifiers: Modifier[] | undefined): MethodDeclaration {
|
||||
const newMethodDeclaration = createNode(SyntaxKind.CallSignature) as SignatureDeclaration;
|
||||
newMethodDeclaration.parent = enclosingDeclaration;
|
||||
newMethodDeclaration.name = signatures[0].getDeclaration().name;
|
||||
|
||||
let maxNonRestArgs = -1;
|
||||
let maxArgsIndex = 0;
|
||||
@@ -100,61 +146,97 @@ namespace ts.codefix {
|
||||
minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount);
|
||||
hasRestParameter = hasRestParameter || sig.hasRestParameter;
|
||||
const nonRestLength = sig.parameters.length - (sig.hasRestParameter ? 1 : 0);
|
||||
if (nonRestLength > maxNonRestArgs) {
|
||||
if (nonRestLength >= maxNonRestArgs) {
|
||||
maxNonRestArgs = nonRestLength;
|
||||
maxArgsIndex = i;
|
||||
}
|
||||
}
|
||||
const maxArgsParameterSymbolNames = signatures[maxArgsIndex].getParameters().map(symbol => symbol.getName());
|
||||
|
||||
const optionalToken = createToken(SyntaxKind.QuestionToken);
|
||||
|
||||
newSignatureDeclaration.parameters = createNodeArray<ParameterDeclaration>();
|
||||
const parameters = createNodeArray<ParameterDeclaration>();
|
||||
for (let i = 0; i < maxNonRestArgs; i++) {
|
||||
const newParameter = createParameterDeclarationWithoutType(i, minArgumentCount, newSignatureDeclaration);
|
||||
newSignatureDeclaration.parameters.push(newParameter);
|
||||
const newParameter = createParameter(
|
||||
/*decorators*/ undefined
|
||||
, /*modifiers*/ undefined
|
||||
, /*dotDotDotToken*/ undefined
|
||||
, maxArgsParameterSymbolNames[i]
|
||||
, /*questionToken*/ i >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined
|
||||
, /*type*/ undefined
|
||||
, /*initializer*/ undefined);
|
||||
parameters.push(newParameter);
|
||||
}
|
||||
|
||||
if (hasRestParameter) {
|
||||
const restParameter = createParameterDeclarationWithoutType(maxNonRestArgs, minArgumentCount, newSignatureDeclaration);
|
||||
restParameter.dotDotDotToken = createToken(SyntaxKind.DotDotDotToken);
|
||||
newSignatureDeclaration.parameters.push(restParameter);
|
||||
const restParameter = createParameter(
|
||||
/*decorators*/ undefined
|
||||
, /*modifiers*/ undefined
|
||||
, createToken(SyntaxKind.DotDotDotToken)
|
||||
, maxArgsParameterSymbolNames[maxNonRestArgs] || "rest"
|
||||
, /*questionToken*/ maxNonRestArgs >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined
|
||||
, /*type*/ undefined
|
||||
, /*initializer*/ undefined);
|
||||
parameters.push(restParameter);
|
||||
}
|
||||
|
||||
return checker.getSignatureFromDeclaration(newSignatureDeclaration);
|
||||
|
||||
function createParameterDeclarationWithoutType(index: number, minArgCount: number, enclosingSignatureDeclaration: SignatureDeclaration): ParameterDeclaration {
|
||||
const newParameter = createNode(SyntaxKind.Parameter) as ParameterDeclaration;
|
||||
|
||||
newParameter.symbol = new SymbolConstructor(SymbolFlags.FunctionScopedVariable, maxArgsParameterSymbolNames[index] || "rest");
|
||||
newParameter.symbol.valueDeclaration = newParameter;
|
||||
newParameter.symbol.declarations = [newParameter];
|
||||
newParameter.parent = enclosingSignatureDeclaration;
|
||||
if (index >= minArgCount) {
|
||||
newParameter.questionToken = optionalToken;
|
||||
}
|
||||
|
||||
return newParameter;
|
||||
}
|
||||
return createMethod(
|
||||
/*decorators*/ undefined
|
||||
, modifiers
|
||||
, /*asteriskToken*/ undefined
|
||||
, name
|
||||
, /*typeParameters*/undefined
|
||||
, parameters
|
||||
, /*type*/ undefined
|
||||
, /*body*/undefined);
|
||||
}
|
||||
|
||||
export function getStubbedMethod(visibility: string, name: string, sigString = "()", newlineChar: string): string {
|
||||
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
export function createStubbedMethod(modifiers: Modifier[], name: string, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType?: TypeNode) {
|
||||
return createMethod(
|
||||
/*decorators*/undefined
|
||||
, /*modifiers*/modifiers
|
||||
, /*asteriskToken*/undefined
|
||||
, name
|
||||
, typeParameters
|
||||
, parameters
|
||||
, returnType
|
||||
, createStubbedMethodBody());
|
||||
}
|
||||
|
||||
function getMethodBodyStub(newlineChar: string) {
|
||||
return ` {${newlineChar}throw new Error('Method not implemented.');${newlineChar}}${newlineChar}`;
|
||||
function createStubbedMethodBody() {
|
||||
return createBlock(
|
||||
[createThrow(
|
||||
createNew(
|
||||
createIdentifier('Error')
|
||||
, /*typeArguments*/undefined
|
||||
, [createLiteral('Method not implemented.')]))]
|
||||
, /*multiline*/true);
|
||||
}
|
||||
|
||||
function getVisibilityPrefixWithSpace(flags: ModifierFlags): string {
|
||||
function createVisibilityModifier(flags: ModifierFlags) {
|
||||
if (flags & ModifierFlags.Public) {
|
||||
return "public ";
|
||||
return createToken(SyntaxKind.PublicKeyword);
|
||||
}
|
||||
else if (flags & ModifierFlags.Protected) {
|
||||
return "protected ";
|
||||
return createToken(SyntaxKind.ProtectedKeyword);
|
||||
}
|
||||
return "";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const SymbolConstructor = objectAllocator.getSymbolConstructor();
|
||||
function createParameterDeclarationFromSymbol(parameterSymbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker) {
|
||||
const parameterDeclaration = parameterSymbol.getDeclarations()[0] as ParameterDeclaration;
|
||||
const parameterType = checker.getTypeOfSymbolAtLocation(parameterSymbol, enclosingDeclaration);
|
||||
const parameterTypeNode = checker.createTypeNode(parameterType);
|
||||
// TODO: deep cloning of decorators/any node.
|
||||
const parameterNode = createParameter(
|
||||
parameterDeclaration.decorators && parameterDeclaration.decorators.map(getSynthesizedClone)
|
||||
, parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone)
|
||||
, parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken)
|
||||
, parameterDeclaration.name
|
||||
, parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken)
|
||||
, parameterTypeNode);
|
||||
return parameterNode;
|
||||
}
|
||||
|
||||
export function getNameFromIndexInfo(info: IndexInfo) {
|
||||
return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : "x"
|
||||
}
|
||||
}
|
||||
@@ -25,17 +25,17 @@ namespace ts.codefix {
|
||||
const forStatement = <ForStatement>token.parent.parent.parent;
|
||||
const forInitializer = <VariableDeclarationList>forStatement.initializer;
|
||||
if (forInitializer.declarations.length === 1) {
|
||||
return createCodeFixToRemoveNode(forInitializer);
|
||||
return deleteNode(forInitializer);
|
||||
}
|
||||
else {
|
||||
return removeSingleItem(forInitializer.declarations, token);
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forOfStatement = <ForOfStatement>token.parent.parent.parent;
|
||||
if (forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
||||
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
|
||||
return createCodeFix("{}", forOfInitializer.declarations[0].getStart(), forOfInitializer.declarations[0].getWidth());
|
||||
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -47,51 +47,59 @@ namespace ts.codefix {
|
||||
case SyntaxKind.CatchClause:
|
||||
const catchClause = <CatchClause>token.parent.parent;
|
||||
const parameter = catchClause.variableDeclaration.getChildren()[0];
|
||||
return createCodeFixToRemoveNode(parameter);
|
||||
return deleteNode(parameter);
|
||||
|
||||
default:
|
||||
const variableStatement = <VariableStatement>token.parent.parent.parent;
|
||||
if (variableStatement.declarationList.declarations.length === 1) {
|
||||
return createCodeFixToRemoveNode(variableStatement);
|
||||
return deleteNode(variableStatement);
|
||||
}
|
||||
else {
|
||||
const declarations = variableStatement.declarationList.declarations;
|
||||
return removeSingleItem(declarations, token);
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
}
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
|
||||
if (typeParameters.length === 1) {
|
||||
return createCodeFix("", token.parent.pos - 1, token.parent.end - token.parent.pos + 2);
|
||||
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
|
||||
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
|
||||
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
return deleteNodeRange(previousToken, nextToken);
|
||||
}
|
||||
else {
|
||||
return removeSingleItem(typeParameters, token);
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case ts.SyntaxKind.Parameter:
|
||||
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
|
||||
if (functionDeclaration.parameters.length === 1) {
|
||||
return createCodeFixToRemoveNode(token.parent);
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else {
|
||||
return removeSingleItem(functionDeclaration.parameters, token);
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where 'import a = A;'
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importEquals = findImportDeclaration(token);
|
||||
return createCodeFixToRemoveNode(importEquals);
|
||||
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
|
||||
return deleteNode(importEquals);
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = <NamedImports>token.parent.parent;
|
||||
if (namedImports.elements.length === 1) {
|
||||
// Only 1 import and it is unused. So the entire declaration should be removed.
|
||||
const importSpec = findImportDeclaration(token);
|
||||
return createCodeFixToRemoveNode(importSpec);
|
||||
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importSpec);
|
||||
}
|
||||
else {
|
||||
return removeSingleItem(namedImports.elements, token);
|
||||
// delete import specifier
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where "import d, * as ns from './file'"
|
||||
@@ -99,98 +107,79 @@ namespace ts.codefix {
|
||||
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
|
||||
const importClause = <ImportClause>token.parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
|
||||
const importDecl = findImportDeclaration(importClause);
|
||||
return createCodeFixToRemoveNode(importDecl);
|
||||
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
// import |d,| * as ns from './file'
|
||||
const start = importClause.name.getStart();
|
||||
let end = findFirstNonSpaceCharPosStarting(importClause.name.end);
|
||||
if (sourceFile.text.charCodeAt(end) === CharacterCodes.comma) {
|
||||
end = findFirstNonSpaceCharPosStarting(end + 1);
|
||||
const start = importClause.name.getStart(sourceFile);
|
||||
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
|
||||
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
||||
// shift first non-whitespace position after comma to the start position of the node
|
||||
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/true) });
|
||||
}
|
||||
else {
|
||||
return deleteNode(importClause.name);
|
||||
}
|
||||
|
||||
return createCodeFix("", start, end - start);
|
||||
}
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
const namespaceImport = <NamespaceImport>token.parent;
|
||||
if (namespaceImport.name == token && !(<ImportClause>namespaceImport.parent).name) {
|
||||
const importDecl = findImportDeclaration(namespaceImport);
|
||||
return createCodeFixToRemoveNode(importDecl);
|
||||
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
const start = (<ImportClause>namespaceImport.parent).name.end;
|
||||
return createCodeFix("", start, (<ImportClause>namespaceImport.parent).namedBindings.end - start);
|
||||
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, /*forDeleteOperation*/ true);
|
||||
return deleteRange({ pos: startPosition, end: namespaceImport.end });
|
||||
}
|
||||
return deleteRange(namespaceImport);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return createCodeFixToRemoveNode(token.parent);
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
if (isDeclarationName(token)) {
|
||||
return createCodeFixToRemoveNode(token.parent);
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(token)) {
|
||||
return createCodeFixToRemoveNode(token.parent.parent);
|
||||
return deleteNode(token.parent.parent);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findImportDeclaration(token: Node): Node {
|
||||
let importDecl = token;
|
||||
while (importDecl.kind != SyntaxKind.ImportDeclaration && importDecl.parent) {
|
||||
importDecl = importDecl.parent;
|
||||
}
|
||||
|
||||
return importDecl;
|
||||
function deleteNode(n: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteNode(sourceFile, n));
|
||||
}
|
||||
|
||||
function createCodeFixToRemoveNode(node: Node) {
|
||||
let end = node.getEnd();
|
||||
const endCharCode = sourceFile.text.charCodeAt(end);
|
||||
const afterEndCharCode = sourceFile.text.charCodeAt(end + 1);
|
||||
if (isLineBreak(endCharCode)) {
|
||||
end += 1;
|
||||
}
|
||||
// in the case of CR LF, you could have two consecutive new line characters for one new line.
|
||||
// this needs to be differenciated from two LF LF chars that actually mean two new lines.
|
||||
if (isLineBreak(afterEndCharCode) && endCharCode !== afterEndCharCode) {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
const start = node.getStart();
|
||||
return createCodeFix("", start, end - start);
|
||||
function deleteRange(range: TextRange) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteRange(sourceFile, range));
|
||||
}
|
||||
|
||||
function findFirstNonSpaceCharPosStarting(start: number) {
|
||||
while (isWhiteSpace(sourceFile.text.charCodeAt(start))) {
|
||||
start += 1;
|
||||
}
|
||||
return start;
|
||||
function deleteNodeInList(n: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteNodeInList(sourceFile, n));
|
||||
}
|
||||
|
||||
function createCodeFix(newText: string, start: number, length: number): CodeAction[] {
|
||||
function deleteNodeRange(start: Node, end: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).deleteNodeRange(sourceFile, start, end));
|
||||
}
|
||||
|
||||
function replaceNode(n: Node, newNode: Node) {
|
||||
return makeChange(textChanges.ChangeTracker.fromCodeFixContext(context).replaceNode(sourceFile, n, newNode));
|
||||
}
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker) {
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), { 0: token.getText() }),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{ newText, span: { start, length } }]
|
||||
}]
|
||||
changes: changeTracker.getChanges()
|
||||
}];
|
||||
}
|
||||
|
||||
function removeSingleItem<T extends Node>(elements: NodeArray<T>, token: T): CodeAction[] {
|
||||
if (elements[0] === token.parent) {
|
||||
return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos + 1);
|
||||
}
|
||||
else {
|
||||
return createCodeFix("", token.parent.pos - 1, token.parent.end - token.parent.pos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -314,24 +314,55 @@ namespace ts.formatting {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function formatNode(node: Node, sourceFileLike: SourceFileLike, languageVariant: LanguageVariant, initialIndentation: number, delta: number, rulesProvider: RulesProvider): TextChange[] {
|
||||
const range = { pos: 0, end: sourceFileLike.text.length };
|
||||
return formatSpanWorker(
|
||||
range,
|
||||
node,
|
||||
initialIndentation,
|
||||
delta,
|
||||
getFormattingScanner(sourceFileLike.text, languageVariant, range.pos, range.end),
|
||||
rulesProvider.getFormatOptions(),
|
||||
rulesProvider,
|
||||
FormattingRequestKind.FormatSelection,
|
||||
_ => false, // assume that node does not have any errors
|
||||
sourceFileLike);
|
||||
}
|
||||
|
||||
function formatSpan(originalRange: TextRange,
|
||||
sourceFile: SourceFile,
|
||||
options: FormatCodeSettings,
|
||||
rulesProvider: RulesProvider,
|
||||
requestKind: FormattingRequestKind): TextChange[] {
|
||||
// find the smallest node that fully wraps the range and compute the initial indentation for the node
|
||||
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
|
||||
return formatSpanWorker(
|
||||
originalRange,
|
||||
enclosingNode,
|
||||
SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options),
|
||||
getOwnOrInheritedDelta(enclosingNode, options, sourceFile),
|
||||
getFormattingScanner(sourceFile.text, sourceFile.languageVariant, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end),
|
||||
options,
|
||||
rulesProvider,
|
||||
requestKind,
|
||||
prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange),
|
||||
sourceFile);
|
||||
}
|
||||
|
||||
const rangeContainsError = prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange);
|
||||
function formatSpanWorker(originalRange: TextRange,
|
||||
enclosingNode: Node,
|
||||
initialIndentation: number,
|
||||
delta: number,
|
||||
formattingScanner: FormattingScanner,
|
||||
options: FormatCodeSettings,
|
||||
rulesProvider: RulesProvider,
|
||||
requestKind: FormattingRequestKind,
|
||||
rangeContainsError: (r: TextRange) => boolean,
|
||||
sourceFile: SourceFileLike): TextChange[] {
|
||||
|
||||
// formatting context is used by rules provider
|
||||
const formattingContext = new FormattingContext(sourceFile, requestKind);
|
||||
|
||||
// find the smallest node that fully wraps the range and compute the initial indentation for the node
|
||||
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
|
||||
|
||||
const formattingScanner = getFormattingScanner(sourceFile, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end);
|
||||
|
||||
const initialIndentation = SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options);
|
||||
|
||||
let previousRangeHasError: boolean;
|
||||
let previousRange: TextRangeWithKind;
|
||||
let previousParent: Node;
|
||||
@@ -351,7 +382,6 @@ namespace ts.formatting {
|
||||
undecoratedStartLine = sourceFile.getLineAndCharacterOfPosition(getNonDecoratorTokenPosOfNode(enclosingNode, sourceFile)).line;
|
||||
}
|
||||
|
||||
const delta = getOwnOrInheritedDelta(enclosingNode, options, sourceFile);
|
||||
processNode(enclosingNode, enclosingNode, startLine, undecoratedStartLine, initialIndentation, delta);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace ts.formatting {
|
||||
private contextNodeBlockIsOnOneLine: boolean;
|
||||
private nextNodeBlockIsOnOneLine: boolean;
|
||||
|
||||
constructor(public sourceFile: SourceFile, public formattingRequestKind: FormattingRequestKind) {
|
||||
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind) {
|
||||
}
|
||||
|
||||
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {
|
||||
|
||||
@@ -30,11 +30,11 @@ namespace ts.formatting {
|
||||
RescanJsxText,
|
||||
}
|
||||
|
||||
export function getFormattingScanner(sourceFile: SourceFile, startPos: number, endPos: number): FormattingScanner {
|
||||
export function getFormattingScanner(text: string, languageVariant: LanguageVariant, startPos: number, endPos: number): FormattingScanner {
|
||||
Debug.assert(scanner === undefined, "Scanner should be undefined");
|
||||
scanner = sourceFile.languageVariant === LanguageVariant.JSX ? jsxScanner : standardScanner;
|
||||
scanner = languageVariant === LanguageVariant.JSX ? jsxScanner : standardScanner;
|
||||
|
||||
scanner.setText(sourceFile.text);
|
||||
scanner.setText(text);
|
||||
scanner.setTextPos(startPos);
|
||||
|
||||
let wasNewLine = true;
|
||||
|
||||
@@ -24,6 +24,10 @@ namespace ts.formatting {
|
||||
return this.rulesMap;
|
||||
}
|
||||
|
||||
public getFormatOptions(): Readonly<ts.FormatCodeSettings> {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
public ensureUpToDate(options: ts.FormatCodeSettings) {
|
||||
if (!this.options || !ts.compareDataObjects(this.options, options)) {
|
||||
const activeRules = this.createActiveRules(options);
|
||||
|
||||
@@ -8,7 +8,19 @@ namespace ts.formatting {
|
||||
Unknown = -1
|
||||
}
|
||||
|
||||
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorSettings): number {
|
||||
/**
|
||||
* Computed indentation for a given position in source file
|
||||
* @param position - position in file
|
||||
* @param sourceFile - target source file
|
||||
* @param options - set of editor options that control indentation
|
||||
* @param assumeNewLineBeforeCloseBrace - false when getIndentation is called on the text from the real source file.
|
||||
* true - when we need to assume that position is on the newline. This is usefult for codefixes, i.e.
|
||||
* function f() {
|
||||
* |}
|
||||
* when inserting some text after open brace we would like to get the value of indentation as if newline was already there.
|
||||
* However by default indentation at position | will be 0 so 'assumeNewLineBeforeCloseBrace' allows to override this behavior,
|
||||
*/
|
||||
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorSettings, assumeNewLineBeforeCloseBrace = false): number {
|
||||
if (position > sourceFile.text.length) {
|
||||
return getBaseIndentation(options); // past EOF
|
||||
}
|
||||
@@ -71,13 +83,14 @@ namespace ts.formatting {
|
||||
if (positionBelongsToNode(current, position, sourceFile) && shouldIndentChildNode(current, previous)) {
|
||||
currentStart = getStartLineAndCharacterForNode(current, sourceFile);
|
||||
|
||||
if (nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken, current, lineAtPosition, sourceFile)) {
|
||||
indentationDelta = 0;
|
||||
const nextTokenKind = nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken, current, lineAtPosition, sourceFile);
|
||||
if (nextTokenKind !== NextTokenKind.Unknown) {
|
||||
// handle cases when codefix is about to be inserted before the close brace
|
||||
indentationDelta = assumeNewLineBeforeCloseBrace && nextTokenKind === NextTokenKind.CloseBrace ? options.indentSize : 0;
|
||||
}
|
||||
else {
|
||||
indentationDelta = lineAtPosition !== currentStart.line ? options.indentSize : 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -218,15 +231,21 @@ namespace ts.formatting {
|
||||
return findColumnForFirstNonWhitespaceCharacterInLine(currentLineAndChar, sourceFile, options);
|
||||
}
|
||||
|
||||
function nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken: Node, current: Node, lineAtPosition: number, sourceFile: SourceFile): boolean {
|
||||
const enum NextTokenKind {
|
||||
Unknown,
|
||||
OpenBrace,
|
||||
CloseBrace
|
||||
}
|
||||
|
||||
function nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken: Node, current: Node, lineAtPosition: number, sourceFile: SourceFile): NextTokenKind {
|
||||
const nextToken = findNextToken(precedingToken, current);
|
||||
if (!nextToken) {
|
||||
return false;
|
||||
return NextTokenKind.Unknown;
|
||||
}
|
||||
|
||||
if (nextToken.kind === SyntaxKind.OpenBraceToken) {
|
||||
// open braces are always indented at the parent level
|
||||
return true;
|
||||
return NextTokenKind.OpenBrace;
|
||||
}
|
||||
else if (nextToken.kind === SyntaxKind.CloseBraceToken) {
|
||||
// close braces are indented at the parent level if they are located on the same line with cursor
|
||||
@@ -239,17 +258,17 @@ namespace ts.formatting {
|
||||
// $}
|
||||
|
||||
const nextTokenStartLine = getStartLineAndCharacterForNode(nextToken, sourceFile).line;
|
||||
return lineAtPosition === nextTokenStartLine;
|
||||
return lineAtPosition === nextTokenStartLine ? NextTokenKind.CloseBrace : NextTokenKind.Unknown;
|
||||
}
|
||||
|
||||
return false;
|
||||
return NextTokenKind.Unknown;
|
||||
}
|
||||
|
||||
function getStartLineAndCharacterForNode(n: Node, sourceFile: SourceFile): LineAndCharacter {
|
||||
function getStartLineAndCharacterForNode(n: Node, sourceFile: SourceFileLike): LineAndCharacter {
|
||||
return sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
|
||||
}
|
||||
|
||||
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFile): boolean {
|
||||
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFileLike): boolean {
|
||||
if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) {
|
||||
const elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
|
||||
Debug.assert(elseKeyword !== undefined);
|
||||
@@ -261,15 +280,15 @@ namespace ts.formatting {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getContainingList(node: Node, sourceFile: SourceFile): NodeArray<Node> {
|
||||
function getListIfStartEndIsInListRange(list: NodeArray<Node>, start: number, end: number) {
|
||||
return list && rangeContainsStartEnd(list, start, end) ? list : undefined;
|
||||
}
|
||||
|
||||
export function getContainingList(node: Node, sourceFile: SourceFile): NodeArray<Node> {
|
||||
if (node.parent) {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
if ((<TypeReferenceNode>node.parent).typeArguments &&
|
||||
rangeContainsStartEnd((<TypeReferenceNode>node.parent).typeArguments, node.getStart(sourceFile), node.getEnd())) {
|
||||
return (<TypeReferenceNode>node.parent).typeArguments;
|
||||
}
|
||||
break;
|
||||
return getListIfStartEndIsInListRange((<TypeReferenceNode>node.parent).typeArguments, node.getStart(sourceFile), node.getEnd());
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return (<ObjectLiteralExpression>node.parent).properties;
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
@@ -280,30 +299,26 @@ namespace ts.formatting {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.ConstructSignature: {
|
||||
const start = node.getStart(sourceFile);
|
||||
if ((<SignatureDeclaration>node.parent).typeParameters &&
|
||||
rangeContainsStartEnd((<SignatureDeclaration>node.parent).typeParameters, start, node.getEnd())) {
|
||||
return (<SignatureDeclaration>node.parent).typeParameters;
|
||||
}
|
||||
if (rangeContainsStartEnd((<SignatureDeclaration>node.parent).parameters, start, node.getEnd())) {
|
||||
return (<SignatureDeclaration>node.parent).parameters;
|
||||
}
|
||||
break;
|
||||
return getListIfStartEndIsInListRange((<SignatureDeclaration>node.parent).typeParameters, start, node.getEnd()) ||
|
||||
getListIfStartEndIsInListRange((<SignatureDeclaration>node.parent).parameters, start, node.getEnd());
|
||||
}
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return getListIfStartEndIsInListRange((<ClassDeclaration>node.parent).typeParameters, node.getStart(sourceFile), node.getEnd());
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.CallExpression: {
|
||||
const start = node.getStart(sourceFile);
|
||||
if ((<CallExpression>node.parent).typeArguments &&
|
||||
rangeContainsStartEnd((<CallExpression>node.parent).typeArguments, start, node.getEnd())) {
|
||||
return (<CallExpression>node.parent).typeArguments;
|
||||
}
|
||||
if ((<CallExpression>node.parent).arguments &&
|
||||
rangeContainsStartEnd((<CallExpression>node.parent).arguments, start, node.getEnd())) {
|
||||
return (<CallExpression>node.parent).arguments;
|
||||
}
|
||||
break;
|
||||
return getListIfStartEndIsInListRange((<CallExpression>node.parent).typeArguments, start, node.getEnd()) ||
|
||||
getListIfStartEndIsInListRange((<CallExpression>node.parent).arguments, start, node.getEnd());
|
||||
}
|
||||
case SyntaxKind.VariableDeclarationList:
|
||||
return getListIfStartEndIsInListRange((<VariableDeclarationList>node.parent).declarations, node.getStart(sourceFile), node.getEnd());
|
||||
case SyntaxKind.NamedImports:
|
||||
case SyntaxKind.NamedExports:
|
||||
return getListIfStartEndIsInListRange((<NamedImportsOrExports>node.parent).elements, node.getStart(sourceFile), node.getEnd());
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -400,7 +415,7 @@ namespace ts.formatting {
|
||||
value of 'character' for '$' is 3
|
||||
value of 'column' for '$' is 6 (assuming that tab size is 4)
|
||||
*/
|
||||
export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorSettings) {
|
||||
export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFileLike, options: EditorSettings) {
|
||||
let character = 0;
|
||||
let column = 0;
|
||||
for (let pos = startPos; pos < endPos; pos++) {
|
||||
@@ -421,7 +436,7 @@ namespace ts.formatting {
|
||||
return { column, character };
|
||||
}
|
||||
|
||||
export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorSettings): number {
|
||||
export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFileLike, options: EditorSettings): number {
|
||||
return findFirstNonWhitespaceCharacterAndColumn(startPos, endPos, sourceFile, options).column;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
/// <reference path='transpile.ts' />
|
||||
/// <reference path='formatting\formatting.ts' />
|
||||
/// <reference path='formatting\smartIndenter.ts' />
|
||||
/// <reference path='textChanges.ts' />
|
||||
/// <reference path='codeFixProvider.ts' />
|
||||
/// <reference path='codefixes\fixes.ts' />
|
||||
|
||||
@@ -63,7 +64,7 @@ namespace ts {
|
||||
return getSourceFileOfNode(this);
|
||||
}
|
||||
|
||||
public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
|
||||
public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number {
|
||||
return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ namespace ts {
|
||||
return list;
|
||||
}
|
||||
|
||||
private createChildren(sourceFile?: SourceFile) {
|
||||
private createChildren(sourceFile?: SourceFileLike) {
|
||||
let children: Node[];
|
||||
if (this.kind >= SyntaxKind.FirstNode) {
|
||||
scanner.setText((sourceFile || this.getSourceFile()).text);
|
||||
@@ -182,7 +183,7 @@ namespace ts {
|
||||
return this._children[index];
|
||||
}
|
||||
|
||||
public getChildren(sourceFile?: SourceFile): Node[] {
|
||||
public getChildren(sourceFile?: SourceFileLike): Node[] {
|
||||
if (!this._children) this.createChildren(sourceFile);
|
||||
return this._children;
|
||||
}
|
||||
@@ -231,7 +232,7 @@ namespace ts {
|
||||
return getSourceFileOfNode(this);
|
||||
}
|
||||
|
||||
public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
|
||||
public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number {
|
||||
return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
|
||||
}
|
||||
|
||||
@@ -1682,7 +1683,7 @@ namespace ts {
|
||||
return [];
|
||||
}
|
||||
|
||||
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
|
||||
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[] {
|
||||
synchronizeHostData();
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const span = { start, length: end - start };
|
||||
@@ -1700,7 +1701,8 @@ namespace ts {
|
||||
program: program,
|
||||
newLineCharacter: newLineChar,
|
||||
host: host,
|
||||
cancellationToken: cancellationToken
|
||||
cancellationToken: cancellationToken,
|
||||
rulesProvider: getRuleProvider(formatOptions)
|
||||
};
|
||||
|
||||
const fixes = codefix.getFixes(context);
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
/* @internal */
|
||||
namespace ts.textChanges {
|
||||
|
||||
/**
|
||||
* Currently for simplicity we store recovered positions on the node itself.
|
||||
* It can be changed to side-table later if we decide that current design is too invasive.
|
||||
*/
|
||||
function getPos(n: TextRange) {
|
||||
return (<any>n)["__pos"];
|
||||
}
|
||||
|
||||
function setPos(n: TextRange, pos: number) {
|
||||
(<any>n)["__pos"] = pos;
|
||||
}
|
||||
|
||||
function getEnd(n: TextRange) {
|
||||
return (<any>n)["__end"];
|
||||
}
|
||||
|
||||
function setEnd(n: TextRange, end: number) {
|
||||
(<any>n)["__end"] = end;
|
||||
}
|
||||
|
||||
export interface ConfigurableStart {
|
||||
useNonAdjustedStartPosition?: boolean;
|
||||
}
|
||||
export interface ConfigurableEnd {
|
||||
useNonAdjustedEndPosition?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usually node.pos points to a position immediately after the previous token.
|
||||
* If this position is used as a beginning of the span to remove - it might lead to removing the trailing trivia of the previous node, i.e:
|
||||
* const x; // this is x
|
||||
* ^ - pos for the next variable declaration will point here
|
||||
* const y; // this is y
|
||||
* ^ - end for previous variable declaration
|
||||
* Usually leading trivia of the variable declaration 'y' should not include trailing trivia (whitespace, comment 'this is x' and newline) from the preceding
|
||||
* variable declaration and trailing trivia for 'y' should include (whitespace, comment 'this is y', newline).
|
||||
* By default when removing nodes we adjust start and end positions to respect specification of the trivia above.
|
||||
* If pos\end should be interpreted literally 'useNonAdjustedStartPosition' or 'useNonAdjustedEndPosition' should be set to true
|
||||
*/
|
||||
export type ConfigurableStartEnd = ConfigurableStart & ConfigurableEnd;
|
||||
|
||||
export interface InsertNodeOptions {
|
||||
/**
|
||||
* Set this value to true to make sure that node text of newly inserted node ends with new line
|
||||
*/
|
||||
insertTrailingNewLine?: boolean;
|
||||
/**
|
||||
* Set this value to true to make sure that node text of newly inserted node starts with new line
|
||||
*/
|
||||
insertLeadingNewLine?: boolean;
|
||||
/**
|
||||
* Text of inserted node will be formatted with this indentation, otherwise indentation will be inferred from the old node
|
||||
*/
|
||||
indentation?: number;
|
||||
/**
|
||||
* Text of inserted node will be formatted with this delta, otherwise delta will be inferred from the new node kind
|
||||
*/
|
||||
delta?: number;
|
||||
}
|
||||
|
||||
export type ChangeNodeOptions = ConfigurableStartEnd & InsertNodeOptions;
|
||||
|
||||
interface Change {
|
||||
readonly sourceFile: SourceFile;
|
||||
readonly range: TextRange;
|
||||
readonly oldNode?: Node;
|
||||
readonly node?: Node;
|
||||
readonly options?: ChangeNodeOptions;
|
||||
}
|
||||
|
||||
export function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart, forDeleteOperation: boolean) {
|
||||
if (options.useNonAdjustedStartPosition) {
|
||||
return node.getFullStart();
|
||||
}
|
||||
const fullStart = node.getFullStart();
|
||||
const start = node.getStart(sourceFile);
|
||||
if (fullStart === start) {
|
||||
return start;
|
||||
}
|
||||
const fullStartLine = getLineStartPositionForPosition(fullStart, sourceFile);
|
||||
const startLine = getLineStartPositionForPosition(start, sourceFile);
|
||||
if (startLine === fullStartLine) {
|
||||
// full start and start of the node are on the same line
|
||||
// a, b;
|
||||
// ^ ^
|
||||
// | start
|
||||
// fullstart
|
||||
// when b is replaced - we usually want to keep the leading trvia
|
||||
// when b is deleted - we delete it
|
||||
return forDeleteOperation ? fullStart : start;
|
||||
}
|
||||
// get start position of the line following the line that contains fullstart position
|
||||
let adjustedStartPosition = getStartPositionOfLine(getLineOfLocalPosition(sourceFile, fullStartLine) + 1, sourceFile);
|
||||
// skip whitespaces/newlines
|
||||
adjustedStartPosition = skipTrivia(sourceFile.text, adjustedStartPosition, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
||||
return getStartPositionOfLine(getLineOfLocalPosition(sourceFile, adjustedStartPosition), sourceFile);
|
||||
}
|
||||
|
||||
export function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) {
|
||||
if (options.useNonAdjustedEndPosition) {
|
||||
return node.getEnd();
|
||||
}
|
||||
const end = node.getEnd();
|
||||
const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true);
|
||||
// check if last character before newPos is linebreak
|
||||
// if yes - considered all skipped trivia to be trailing trivia of the node
|
||||
return newEnd !== end && isLineBreak(sourceFile.text.charCodeAt(newEnd - 1))
|
||||
? newEnd
|
||||
: end;
|
||||
}
|
||||
|
||||
function isSeparator(node: Node, separator: Node): boolean {
|
||||
return node.parent && (separator.kind === SyntaxKind.CommaToken || (separator.kind === SyntaxKind.SemicolonToken && node.parent.kind === SyntaxKind.ObjectLiteralExpression));
|
||||
}
|
||||
|
||||
export class ChangeTracker {
|
||||
private changes: Change[] = [];
|
||||
private readonly newLineCharacter: string;
|
||||
|
||||
public static fromCodeFixContext(context: CodeFixContext) {
|
||||
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly newLine: NewLineKind,
|
||||
private readonly rulesProvider: formatting.RulesProvider,
|
||||
private readonly validator?: (text: NonFormattedText) => void) {
|
||||
this.newLineCharacter = getNewLineCharacter({ newLine });
|
||||
}
|
||||
|
||||
public deleteNode(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, node, options, /*forDeleteOperation*/ true);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, node, options);
|
||||
this.changes.push({ sourceFile, options, range: { pos: startPosition, end: endPosition } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public deleteRange(sourceFile: SourceFile, range: TextRange) {
|
||||
this.changes.push({ sourceFile, range });
|
||||
return this;
|
||||
}
|
||||
|
||||
public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, /*forDeleteOperation*/ true);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||
this.changes.push({ sourceFile, options, range: { pos: startPosition, end: endPosition } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public deleteNodeInList(sourceFile: SourceFile, node: Node) {
|
||||
const containingList = formatting.SmartIndenter.getContainingList(node, sourceFile);
|
||||
if (!containingList) {
|
||||
return;
|
||||
}
|
||||
const index = containingList.indexOf(node);
|
||||
if (index < 0) {
|
||||
return this;
|
||||
}
|
||||
if (containingList.length === 1) {
|
||||
this.deleteNode(sourceFile, node);
|
||||
return this;
|
||||
}
|
||||
if (index !== containingList.length - 1) {
|
||||
const nextToken = getTokenAtPosition(sourceFile, node.end);
|
||||
if (nextToken && isSeparator(node, nextToken)) {
|
||||
// find first non-whitespace position in the leading trivia of the node
|
||||
const startPosition = skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, {}, /*forDeleteOperation*/ true), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
||||
const nextElement = containingList[index + 1];
|
||||
/// find first non-whitespace position in the leading trivia of the next node
|
||||
const endPosition = skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, nextElement, {}, /*forDeleteOperation*/ true), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
||||
// shift next node so its first non-whitespace position will be moved to the first non-whitespace position of the deleted node
|
||||
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||
}
|
||||
}
|
||||
else {
|
||||
const previousToken = getTokenAtPosition(sourceFile, containingList[index - 1].end);
|
||||
if (previousToken && isSeparator(node, previousToken)) {
|
||||
this.deleteNodeRange(sourceFile, previousToken, node);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
this.changes.push({ sourceFile, range, options, node: newNode });
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, /*forDeleteOperation*/ false);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options);
|
||||
this.changes.push({ sourceFile, options, oldNode, node: newNode, range: { pos: startPosition, end: endPosition } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, /*forDeleteOperation*/ false);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||
this.changes.push({ sourceFile, options, oldNode: startNode, node: newNode, range: { pos: startPosition, end: endPosition } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
this.changes.push({ sourceFile, options, node: newNode, range: { pos: pos, end: pos } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, options: InsertNodeOptions & ConfigurableStart = {}) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, before, options, /*forDeleteOperation*/ false);
|
||||
this.changes.push({ sourceFile, options, oldNode: before, node: newNode, range: { pos: startPosition, end: startPosition } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node, options: InsertNodeOptions & ConfigurableEnd = {}) {
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, after, options);
|
||||
this.changes.push({ sourceFile, options, oldNode: after, node: newNode, range: { pos: endPosition, end: endPosition } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public getChanges(): FileTextChanges[] {
|
||||
const changesPerFile = createFileMap<Change[]>();
|
||||
// group changes per file
|
||||
for (const c of this.changes) {
|
||||
let changesInFile = changesPerFile.get(c.sourceFile.path);
|
||||
if (!changesInFile) {
|
||||
changesPerFile.set(c.sourceFile.path, changesInFile = []);
|
||||
};
|
||||
changesInFile.push(c);
|
||||
}
|
||||
// convert changes
|
||||
const fileChangesList: FileTextChanges[] = [];
|
||||
changesPerFile.forEachValue(path => {
|
||||
const changesInFile = changesPerFile.get(path);
|
||||
const sourceFile = changesInFile[0].sourceFile;
|
||||
ChangeTracker.normalize(changesInFile);
|
||||
|
||||
const fileTextChanges: FileTextChanges = { fileName: sourceFile.fileName, textChanges: [] };
|
||||
for (const c of changesInFile) {
|
||||
fileTextChanges.textChanges.push({
|
||||
span: this.computeSpan(c, sourceFile),
|
||||
newText: this.computeNewText(c, sourceFile)
|
||||
});
|
||||
}
|
||||
fileChangesList.push(fileTextChanges);
|
||||
});
|
||||
|
||||
return fileChangesList;
|
||||
}
|
||||
|
||||
private computeSpan(change: Change, _sourceFile: SourceFile): TextSpan {
|
||||
return createTextSpanFromBounds(change.range.pos, change.range.end);
|
||||
}
|
||||
|
||||
private computeNewText(change: Change, sourceFile: SourceFile): string {
|
||||
if (!change.node) {
|
||||
// deletion case
|
||||
return "";
|
||||
}
|
||||
const options = change.options || {};
|
||||
const nonFormattedText = getNonformattedText(change.node, sourceFile, this.newLine);
|
||||
if (this.validator) {
|
||||
this.validator(nonFormattedText);
|
||||
}
|
||||
|
||||
const formatOptions = this.rulesProvider.getFormatOptions();
|
||||
const pos = change.range.pos;
|
||||
const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos;
|
||||
|
||||
const initialIndentation =
|
||||
change.options.indentation !== undefined
|
||||
? change.options.indentation
|
||||
: change.oldNode
|
||||
? formatting.SmartIndenter.getIndentation(change.range.pos, sourceFile, formatOptions, posStartsLine || change.options.insertLeadingNewLine)
|
||||
: 0;
|
||||
const delta =
|
||||
change.options.delta !== undefined
|
||||
? change.options.delta
|
||||
: formatting.SmartIndenter.shouldIndentChildNode(change.node)
|
||||
? formatOptions.indentSize
|
||||
: 0;
|
||||
|
||||
let text = applyFormatting(nonFormattedText, sourceFile, initialIndentation, delta, this.rulesProvider);
|
||||
// strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line
|
||||
text = posStartsLine ? text : text.replace(/^\s+/, "");
|
||||
|
||||
if (options.insertLeadingNewLine) {
|
||||
text = this.newLineCharacter + text;
|
||||
}
|
||||
if (options.insertTrailingNewLine) {
|
||||
text = text + this.newLineCharacter;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static normalize(changes: Change[]) {
|
||||
// order changes by start position
|
||||
changes.sort((a, b) => a.range.pos - b.range.pos);
|
||||
// verify that end position of the change is less than start position of the next change
|
||||
for (let i = 0; i < changes.length - 2; i++) {
|
||||
Debug.assert(changes[i].range.end <= changes[i + 1].range.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface NonFormattedText {
|
||||
readonly text: string;
|
||||
readonly node: Node;
|
||||
}
|
||||
|
||||
export function getNonformattedText(node: Node, sourceFile: SourceFile, newLine: NewLineKind): NonFormattedText {
|
||||
const options = { newLine, target: sourceFile.languageVersion };
|
||||
const writer = new Writer(getNewLineCharacter(options));
|
||||
const printer = createPrinter(options, writer);
|
||||
printer.writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
return { text: writer.getText(), node: assignPositionsToNode(node) };
|
||||
}
|
||||
|
||||
export function applyFormatting(nonFormattedText: NonFormattedText, sourceFile: SourceFile, initialIndentation: number, delta: number, rulesProvider: formatting.RulesProvider) {
|
||||
const lineMap = computeLineStarts(nonFormattedText.text);
|
||||
const file: SourceFileLike = {
|
||||
text: nonFormattedText.text,
|
||||
lineMap,
|
||||
getLineAndCharacterOfPosition: pos => computeLineAndCharacterOfPosition(lineMap, pos)
|
||||
};
|
||||
const changes = formatting.formatNode(nonFormattedText.node, file, sourceFile.languageVariant, initialIndentation, delta, rulesProvider);
|
||||
return applyChanges(nonFormattedText.text, changes);
|
||||
}
|
||||
|
||||
export function applyChanges(text: string, changes: TextChange[]): string {
|
||||
for (let i = changes.length - 1; i >= 0; i--) {
|
||||
const change = changes[i];
|
||||
text = `${text.substring(0, change.span.start)}${change.newText}${text.substring(textSpanEnd(change.span))}`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function isTrivia(s: string) {
|
||||
return skipTrivia(s, 0) === s.length;
|
||||
}
|
||||
|
||||
function assignPositionsToNode(node: Node): Node {
|
||||
const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray);
|
||||
// create proxy node for non synthesized nodes
|
||||
const newNode = nodeIsSynthesized(visited)
|
||||
? visited
|
||||
: (Proxy.prototype = visited, new (<any>Proxy)());
|
||||
newNode.pos = getPos(node);
|
||||
newNode.end = getEnd(node);
|
||||
return newNode;
|
||||
|
||||
function Proxy() { }
|
||||
}
|
||||
|
||||
function assignPositionsToNodeArray(nodes: NodeArray<any>, visitor: Visitor, test?: (node: Node) => boolean, start?: number, count?: number) {
|
||||
const visited = visitNodes(nodes, visitor, test, start, count);
|
||||
if (!visited) {
|
||||
return visited;
|
||||
}
|
||||
// clone nodearray if necessary
|
||||
const nodeArray = visited === nodes ? createNodeArray(visited) : visited;
|
||||
nodeArray.pos = getPos(nodes);
|
||||
nodeArray.end = getEnd(nodes);
|
||||
return nodeArray;
|
||||
}
|
||||
|
||||
class Writer implements EmitTextWriter, PrintHandlers {
|
||||
private lastNonTriviaPosition = 0;
|
||||
private readonly writer: EmitTextWriter;
|
||||
|
||||
public readonly onEmitNode: PrintHandlers["onEmitNode"];
|
||||
public readonly onBeforeEmitNodeArray: PrintHandlers["onBeforeEmitNodeArray"];
|
||||
public readonly onAfterEmitNodeArray: PrintHandlers["onAfterEmitNodeArray"];
|
||||
|
||||
constructor(newLine: string) {
|
||||
this.writer = createTextWriter(newLine);
|
||||
this.onEmitNode = (hint, node, printCallback) => {
|
||||
setPos(node, this.lastNonTriviaPosition);
|
||||
printCallback(hint, node);
|
||||
setEnd(node, this.lastNonTriviaPosition);
|
||||
};
|
||||
this.onBeforeEmitNodeArray = nodes => {
|
||||
if (nodes) {
|
||||
setPos(nodes, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
this.onAfterEmitNodeArray = nodes => {
|
||||
if (nodes) {
|
||||
setEnd(nodes, this.lastNonTriviaPosition);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private setLastNonTriviaPosition(s: string, force: boolean) {
|
||||
if (force || !isTrivia(s)) {
|
||||
this.lastNonTriviaPosition = this.writer.getTextPos();
|
||||
let i = 0;
|
||||
while (isWhiteSpace(s.charCodeAt(s.length - i - 1))) {
|
||||
i++;
|
||||
}
|
||||
// trim trailing whitespaces
|
||||
this.lastNonTriviaPosition -= i;
|
||||
}
|
||||
}
|
||||
|
||||
write(s: string): void {
|
||||
this.writer.write(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeTextOfNode(text: string, node: Node): void {
|
||||
this.writer.writeTextOfNode(text, node);
|
||||
}
|
||||
writeLine(): void {
|
||||
this.writer.writeLine();
|
||||
}
|
||||
increaseIndent(): void {
|
||||
this.writer.increaseIndent();
|
||||
}
|
||||
decreaseIndent(): void {
|
||||
this.writer.decreaseIndent();
|
||||
}
|
||||
getText(): string {
|
||||
return this.writer.getText();
|
||||
}
|
||||
rawWrite(s: string): void {
|
||||
this.writer.rawWrite(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ false);
|
||||
}
|
||||
writeLiteral(s: string): void {
|
||||
this.writer.writeLiteral(s);
|
||||
this.setLastNonTriviaPosition(s, /*force*/ true);
|
||||
}
|
||||
getTextPos(): number {
|
||||
return this.writer.getTextPos();
|
||||
}
|
||||
getLine(): number {
|
||||
return this.writer.getLine();
|
||||
}
|
||||
getColumn(): number {
|
||||
return this.writer.getColumn();
|
||||
}
|
||||
getIndent(): number {
|
||||
return this.writer.getIndent();
|
||||
}
|
||||
isAtStartOfLine(): boolean {
|
||||
return this.writer.isAtStartOfLine();
|
||||
}
|
||||
reset(): void {
|
||||
this.writer.reset();
|
||||
this.lastNonTriviaPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,7 @@
|
||||
"shims.ts",
|
||||
"signatureHelp.ts",
|
||||
"symbolDisplay.ts",
|
||||
"textChanges.ts",
|
||||
"formatting/formatting.ts",
|
||||
"formatting/formattingContext.ts",
|
||||
"formatting/formattingRequestKind.ts",
|
||||
|
||||
@@ -4,7 +4,11 @@ namespace ts {
|
||||
getChildCount(sourceFile?: SourceFile): number;
|
||||
getChildAt(index: number, sourceFile?: SourceFile): Node;
|
||||
getChildren(sourceFile?: SourceFile): Node[];
|
||||
/* @internal */
|
||||
getChildren(sourceFile?: SourceFileLike): Node[];
|
||||
getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number;
|
||||
/* @internal */
|
||||
getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number;
|
||||
getFullStart(): number;
|
||||
getEnd(): number;
|
||||
getWidth(sourceFile?: SourceFile): number;
|
||||
@@ -59,6 +63,10 @@ namespace ts {
|
||||
update(newText: string, textChangeRange: TextChangeRange): SourceFile;
|
||||
}
|
||||
|
||||
export interface SourceFileLike {
|
||||
getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an immutable snapshot of a script at a specified time.Once acquired, the
|
||||
* snapshot is observably immutable. i.e. the same calls with the same parameters will return
|
||||
@@ -248,7 +256,7 @@ namespace ts {
|
||||
|
||||
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean;
|
||||
|
||||
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[]): CodeAction[];
|
||||
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[];
|
||||
|
||||
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
|
||||
|
||||
|
||||
@@ -394,8 +394,8 @@ namespace ts {
|
||||
list: Node;
|
||||
}
|
||||
|
||||
export function getLineStartPositionForPosition(position: number, sourceFile: SourceFile): number {
|
||||
const lineStarts = sourceFile.getLineStarts();
|
||||
export function getLineStartPositionForPosition(position: number, sourceFile: SourceFileLike): number {
|
||||
const lineStarts = getLineStarts(sourceFile);
|
||||
const line = sourceFile.getLineAndCharacterOfPosition(position).line;
|
||||
return lineStarts[line];
|
||||
}
|
||||
@@ -604,7 +604,7 @@ namespace ts {
|
||||
return !!findChildOfKind(n, kind, sourceFile);
|
||||
}
|
||||
|
||||
export function findChildOfKind(n: Node, kind: SyntaxKind, sourceFile?: SourceFile): Node | undefined {
|
||||
export function findChildOfKind(n: Node, kind: SyntaxKind, sourceFile?: SourceFileLike): Node | undefined {
|
||||
return forEach(n.getChildren(sourceFile), c => c.kind === kind && c);
|
||||
}
|
||||
|
||||
@@ -1003,10 +1003,6 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isToken(n: Node): boolean {
|
||||
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
|
||||
}
|
||||
|
||||
export function isWord(kind: SyntaxKind): boolean {
|
||||
return kind === SyntaxKind.Identifier || isKeyword(kind);
|
||||
}
|
||||
@@ -1384,8 +1380,12 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function getOpenBraceEnd(constructor: ConstructorDeclaration, sourceFile: SourceFile) {
|
||||
export function getOpenBrace(constructor: ConstructorDeclaration, sourceFile: SourceFile) {
|
||||
// First token is the open curly, this is where we want to put the 'super' call.
|
||||
return constructor.body.getFirstToken(sourceFile).getEnd();
|
||||
return constructor.body.getFirstToken(sourceFile);
|
||||
}
|
||||
|
||||
export function getOpenBraceOfClassLike(declaration: ClassLikeDeclaration, sourceFile: SourceFile) {
|
||||
return getTokenAtPosition(sourceFile, declaration.members.pos - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
===ORIGINAL===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
var z = 3; // comment 4
|
||||
@@ -0,0 +1,12 @@
|
||||
===ORIGINAL===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
var x = 1;var z = 3; // comment 4
|
||||
@@ -0,0 +1,14 @@
|
||||
===ORIGINAL===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
// comment 3
|
||||
var z = 3; // comment 4
|
||||
@@ -0,0 +1,13 @@
|
||||
===ORIGINAL===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
var x = 1; // comment 3
|
||||
var z = 3; // comment 4
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
var x = 1; // some comment - 1
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
/**
|
||||
* comment 2
|
||||
*/
|
||||
var y = 2; // comment 3
|
||||
var z = 3; // comment 4
|
||||
@@ -0,0 +1,4 @@
|
||||
===ORIGINAL===
|
||||
var a = 1, b = 2, c = 3;
|
||||
===MODIFIED===
|
||||
var b = 2, c = 3;
|
||||
@@ -0,0 +1,10 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(a: number,b: string,c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(b: string,c = true) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(a: number,b: string,c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(a: number,c = true) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(a: number,b: string,c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(a: number,b: string) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(
|
||||
a: number,
|
||||
b: string,
|
||||
c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(
|
||||
b: string,
|
||||
c = true) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(
|
||||
a: number,
|
||||
b: string,
|
||||
c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(
|
||||
a: number,
|
||||
c = true) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(
|
||||
a: number,
|
||||
b: string,
|
||||
c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(
|
||||
a: number,
|
||||
b: string) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
===ORIGINAL===
|
||||
var a = 1,b = 2,c = 3;
|
||||
===MODIFIED===
|
||||
var b = 2,c = 3;
|
||||
@@ -0,0 +1,4 @@
|
||||
===ORIGINAL===
|
||||
var a = 1, b = 2, c = 3;
|
||||
===MODIFIED===
|
||||
var a = 1, c = 3;
|
||||
@@ -0,0 +1,4 @@
|
||||
===ORIGINAL===
|
||||
var a = 1,b = 2,c = 3;
|
||||
===MODIFIED===
|
||||
var a = 1,c = 3;
|
||||
@@ -0,0 +1,4 @@
|
||||
===ORIGINAL===
|
||||
var a = 1, b = 2, c = 3;
|
||||
===MODIFIED===
|
||||
var a = 1, b = 2;
|
||||
@@ -0,0 +1,4 @@
|
||||
===ORIGINAL===
|
||||
var a = 1,b = 2,c = 3;
|
||||
===MODIFIED===
|
||||
var a = 1,b = 2;
|
||||
@@ -0,0 +1,13 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
var b = 2,
|
||||
c = 3;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
var a = 1, // comment 1
|
||||
// comment 2
|
||||
b = 2, // comment 3
|
||||
// comment 4
|
||||
c = 3; // comment 5
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
var // comment 2
|
||||
b = 2, // comment 3
|
||||
// comment 4
|
||||
c = 3; // comment 5
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
var a = 1,
|
||||
c = 3;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
var a = 1, // comment 1
|
||||
// comment 2
|
||||
b = 2, // comment 3
|
||||
// comment 4
|
||||
c = 3; // comment 5
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
var a = 1, // comment 1
|
||||
// comment 4
|
||||
c = 3; // comment 5
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
var a = 1,
|
||||
b = 2;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
var a = 1, // comment 1
|
||||
// comment 2
|
||||
b = 2, // comment 3
|
||||
// comment 4
|
||||
c = 3; // comment 5
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
var a = 1, // comment 1
|
||||
// comment 2
|
||||
b = 2; // comment 5
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(a: number, b: string, c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(b: string, c = true) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(a: number, b: string, c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(a: number, c = true) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo(a: number, b: string, c = true) {
|
||||
return 1;
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
function foo(a: number, b: string) {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,15 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1;// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,17 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,15 @@
|
||||
===ORIGINAL===
|
||||
|
||||
function foo() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function bar() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
function bar() {
|
||||
return 2;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M
|
||||
{
|
||||
namespace M2
|
||||
{
|
||||
function foo() {
|
||||
// comment 1
|
||||
const x = 1;
|
||||
|
||||
/**
|
||||
* comment 2 line 1
|
||||
* comment 2 line 2
|
||||
*/
|
||||
function f() {
|
||||
return 100;
|
||||
}
|
||||
const y = 2; // comment 3
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M
|
||||
{
|
||||
function bar(): any
|
||||
{
|
||||
/**
|
||||
* comment 2 line 1
|
||||
* comment 2 line 2
|
||||
*/
|
||||
function f()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
const y = 2; // comment 3
|
||||
return 1;
|
||||
}
|
||||
namespace M2
|
||||
{
|
||||
function foo() {
|
||||
// comment 1
|
||||
const x = 1;
|
||||
|
||||
return bar();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
===ORIGINAL===
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
===ORIGINAL===
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
var x = 1;
|
||||
}
|
||||
}
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
var x = 1;
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,20 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var yz1 = {
|
||||
p1: 1
|
||||
}; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,26 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
===MODIFIED===
|
||||
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
namespace M {
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,13 @@
|
||||
===ORIGINAL===
|
||||
|
||||
namespace A {
|
||||
const x = 1, y = "2";
|
||||
}
|
||||
|
||||
===MODIFIED===
|
||||
|
||||
namespace A {
|
||||
const x = 1, z1 = {
|
||||
p1: 1
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1;
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,21 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
// comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,19 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1;public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
} // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,19 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
} // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,19 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,19 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1;
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,20 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
// comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,18 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1;public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
} // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,19 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
var a = 4; // comment 7
|
||||
@@ -0,0 +1,6 @@
|
||||
===ORIGINAL===
|
||||
const x = 1, y = "2";
|
||||
===MODIFIED===
|
||||
const x = 1, z1 = {
|
||||
p1: 1
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
===ORIGINAL===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var y = 2; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7
|
||||
===MODIFIED===
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
var a = 4; // comment 7
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
////class C {
|
||||
//// foo: number;
|
||||
//// constructor() {
|
||||
//// [|foo = 10|];
|
||||
//// }
|
||||
//// constructor() {[|
|
||||
//// foo = 10;
|
||||
//// |]}
|
||||
////}
|
||||
|
||||
verify.rangeAfterCodeFix("this.foo = 10");
|
||||
verify.rangeAfterCodeFix(`
|
||||
this.foo = 10;
|
||||
`, /*includeWhitespace*/ true);
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
f(): void{
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -15,6 +15,6 @@ verify.rangeAfterCodeFix(`
|
||||
f(a: string, b: number): Function;
|
||||
f(a: string): Function;
|
||||
f(a: any, b?: any) {
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
f(): this {
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
+2
-3
@@ -4,10 +4,9 @@
|
||||
//// abstract f(x: T): T;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A<number> {[|
|
||||
//// |]}
|
||||
//// class C extends A<number> {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`f(x: number): number{
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
@@ -4,10 +4,9 @@
|
||||
//// abstract f(x: T): T;
|
||||
//// }
|
||||
////
|
||||
//// class C<U> extends A<U> {[|
|
||||
//// |]}
|
||||
//// class C<U> extends A<U> {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`f(x: U): U{
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
@@ -4,8 +4,7 @@
|
||||
//// private abstract x: number;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A {[|
|
||||
//// |]}
|
||||
//// class C extends A {[| |]}
|
||||
|
||||
// We don't know how to fix this problem. We can:
|
||||
// 1) Make x protected, and then insert.
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
//// abstract foo(): number;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A {[|
|
||||
//// |]}
|
||||
//// class C extends A {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
x: number;
|
||||
y: this;
|
||||
z: A;
|
||||
foo(): number {
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
//// protected abstract x: number;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A {[|
|
||||
//// |]}
|
||||
//// class C extends A {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
protected x: number;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//// public abstract x: number;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A {[|
|
||||
//// |]}
|
||||
//// class C extends A {[| |]}
|
||||
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
public x: number;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
//// abstract z: number;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A {[| |]
|
||||
//// constructor(public x: number) { super(); }
|
||||
//// class C extends A {[|
|
||||
//// |]constructor(public x: number) { super(); }
|
||||
//// y: number;
|
||||
//// }
|
||||
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
f(): void{
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
////
|
||||
//// }
|
||||
////
|
||||
//// class C3 implements C2 {[|
|
||||
//// class C3 implements C2 {[|
|
||||
//// |]f2(){}
|
||||
//// }
|
||||
|
||||
verify.rangeAfterCodeFix(`f1(): void{
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
verify.rangeAfterCodeFix(`
|
||||
method(a: number, b: string): boolean;
|
||||
method(a: string | number, b?: string | number): boolean | Function {
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user