From b4a382bdd2a9cb25555e1f852b9e759372bc996f Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 11 Jan 2018 14:53:31 -0800 Subject: [PATCH] Stop explicitly storing newline in refactoring/code fix contexts It's already in the EditorSettings and the LanguageServiceHost. Fixes #18291 Fixes #18445 --- src/harness/unittests/extractTestHelpers.ts | 2 -- src/services/codeFixProvider.ts | 5 ++-- .../addMissingInvocationForDecorator.ts | 2 +- ...correctQualifiedNameToIndexedAccessType.ts | 2 +- .../codefixes/disableJsDiagnostics.ts | 6 +++-- src/services/codefixes/fixAddMissingMember.ts | 14 +++++----- .../codefixes/fixAwaitInSyncFunction.ts | 2 +- ...sDoesntImplementInheritedAbstractMember.ts | 2 +- .../fixClassIncorrectlyImplementsInterface.ts | 2 +- .../fixClassSuperMustPrecedeThisAccess.ts | 2 +- .../fixConstructorForDerivedNeedSuperCall.ts | 2 +- .../fixExtendsInterfaceBecomesImplements.ts | 2 +- .../fixForgottenThisPropertyAccess.ts | 2 +- .../codefixes/fixInvalidImportSyntax.ts | 6 ++--- src/services/codefixes/fixSpelling.ts | 2 +- src/services/codefixes/fixUnusedIdentifier.ts | 4 +-- src/services/codefixes/importFixes.ts | 27 +++++++++---------- src/services/completions.ts | 1 - src/services/refactorProvider.ts | 20 ++++++++++++-- .../refactors/annotateWithTypeFromJSDoc.ts | 4 +-- .../refactors/convertFunctionToEs6Class.ts | 2 +- src/services/refactors/convertToEs6Module.ts | 2 +- src/services/refactors/extractSymbol.ts | 4 +-- src/services/refactors/useDefaultImport.ts | 2 +- src/services/services.ts | 7 ++--- 25 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index a04f443c3c9..f519555bd26 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -121,7 +121,6 @@ namespace ts { const sourceFile = program.getSourceFile(path); const context: RefactorContext = { cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse }, - newLineCharacter, program, file: sourceFile, startPosition: selectionRange.start, @@ -185,7 +184,6 @@ namespace ts { const sourceFile = program.getSourceFile(f.path); const context: RefactorContext = { cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse }, - newLineCharacter, program, file: sourceFile, startPosition: selectionRange.start, diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index aa1f2fb6885..619bcc8813c 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -7,10 +7,9 @@ namespace ts { getAllCodeActions?(context: CodeFixAllContext): CombinedCodeActions; } - export interface CodeFixContextBase extends textChanges.TextChangesContext { + export interface CodeFixContextBase extends RefactorOrCodeFixContext { sourceFile: SourceFile; program: Program; - host: LanguageServiceHost; cancellationToken: CancellationToken; } @@ -84,7 +83,7 @@ namespace ts { export function codeFixAll(context: CodeFixAllContext, errorCodes: number[], use: (changes: textChanges.ChangeTracker, error: Diagnostic, commands: Push) => void): CombinedCodeActions { const commands: CodeActionCommand[] = []; - const changes = textChanges.ChangeTracker.with(context, t => + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands))); return createCombinedCodeActions(changes, commands.length === 0 ? undefined : commands); } diff --git a/src/services/codefixes/addMissingInvocationForDecorator.ts b/src/services/codefixes/addMissingInvocationForDecorator.ts index d063df87be4..25214f08218 100644 --- a/src/services/codefixes/addMissingInvocationForDecorator.ts +++ b/src/services/codefixes/addMissingInvocationForDecorator.ts @@ -5,7 +5,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions: (context) => { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => makeChange(t, context.sourceFile, context.span.start)); return [{ description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts index de4498035be..076ea6135ae 100644 --- a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts +++ b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const qualifiedName = getQualifiedName(context.sourceFile, context.span.start); if (!qualifiedName) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, context.sourceFile, qualifiedName)); const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${qualifiedName.left.text}["${qualifiedName.right.text}"]`]); return [{ description, changes, fixId }]; }, diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 6a59e61d52d..cf006a6066c 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -9,12 +9,14 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions(context) { - const { sourceFile, program, newLineCharacter, span } = context; + const { sourceFile, program, span } = context; if (!isInJavaScriptFile(sourceFile) || !isCheckJsEnabledForFile(sourceFile, program.getCompilerOptions())) { return undefined; } + const newLineCharacter = getNewLineFromContext(context); + return [{ description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message), changes: [createFileTextChanges(sourceFile.fileName, [getIgnoreCommentLocationForLocation(sourceFile, span.start, newLineCharacter)])], @@ -36,7 +38,7 @@ namespace ts.codefix { fixIds: [fixId], // No point applying as a group, doing it once will fix all errors getAllCodeActions: context => codeFixAllWithTextChanges(context, errorCodes, (changes, err) => { if (err.start !== undefined) { - changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, context.newLineCharacter)); + changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, getNewLineFromContext(context))); } }), }); diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 0fc6a430e19..8e45efa09ad 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -96,7 +96,7 @@ namespace ts.codefix { } function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined { - const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]); return { description, changes, fixId }; @@ -142,9 +142,9 @@ namespace ts.codefix { return typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); } - function createAddPropertyDeclarationAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { + function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]); - const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); return { description, changes, fixId }; } @@ -159,7 +159,7 @@ namespace ts.codefix { changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, property); } - function createAddIndexSignatureAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction { + function createAddIndexSignatureAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction { // Index signatures cannot have the static modifier. const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword); const indexingParameter = createParameter( @@ -176,14 +176,14 @@ namespace ts.codefix { [indexingParameter], typeNode); - const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); // No fixId here because code-fix-all currently only works on adding individual named properties. return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined }; } - function getActionForMethodDeclaration(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { + function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]); - const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); return { description, changes, fixId }; } diff --git a/src/services/codefixes/fixAwaitInSyncFunction.ts b/src/services/codefixes/fixAwaitInSyncFunction.ts index 883993e7b51..7d09e5f214f 100644 --- a/src/services/codefixes/fixAwaitInSyncFunction.ts +++ b/src/services/codefixes/fixAwaitInSyncFunction.ts @@ -11,7 +11,7 @@ namespace ts.codefix { const { sourceFile, span } = context; const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, nodes)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_async_modifier_to_containing_function), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index da3685339ab..6e215666ddf 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -9,7 +9,7 @@ namespace ts.codefix { errorCodes, getCodeActions(context) { const { program, sourceFile, span } = context; - const changes = textChanges.ChangeTracker.with(context, t => + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t)); return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }]; }, diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 0236b0c79b7..1eb5bfac986 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -10,7 +10,7 @@ namespace ts.codefix { const classDeclaration = getClass(sourceFile, span.start); const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); return { description, changes, fixId }; diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 1595bbf3c13..76c1c588097 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -9,7 +9,7 @@ namespace ts.codefix { const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; const { constructor, superCall } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, constructor, superCall)); return [{ description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts index 8fe2e8458a8..0b747344e3b 100644 --- a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts +++ b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile, span } = context; const ctr = getNode(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, ctr)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts index d98ca556f47..a4e91410db5 100644 --- a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts +++ b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts @@ -9,7 +9,7 @@ namespace ts.codefix { const nodes = getNodes(sourceFile, context.span.start); if (!nodes) return undefined; const { extendsToken, heritageClauses } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChanges(t, sourceFile, extendsToken, heritageClauses)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChanges(t, sourceFile, extendsToken, heritageClauses)); return [{ description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index 19610da0b15..8c337c16c4f 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile } = context; const token = getNode(sourceFile, context.span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, token)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, token)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_this_to_unresolved_variable), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index f98f1eaea7c..0bfc6a6090d 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -32,7 +32,7 @@ namespace ts.codefix { createImportClause(namespace.name, /*namedBindings*/ undefined), node.moduleSpecifier ); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); changeTracker.replaceNode(sourceFile, node, replacement, { useNonAdjustedEndPosition: true }); const changes = changeTracker.getChanges(); variations.push({ @@ -48,7 +48,7 @@ namespace ts.codefix { namespace.name, createExternalModuleReference(node.moduleSpecifier) ); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); changeTracker.replaceNode(sourceFile, node, replacement, { useNonAdjustedEndPosition: true }); const changes = changeTracker.getChanges(); variations.push({ @@ -86,7 +86,7 @@ namespace ts.codefix { addRange(fixes, getCodeFixesForImportDeclaration(context, relatedImport)); } const propertyAccess = createPropertyAccess(expr, "default"); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); changeTracker.replaceNode(sourceFile, expr, propertyAccess, {}); const changes = changeTracker.getChanges(); fixes.push({ diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 729f14e9ef9..95159012f48 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -12,7 +12,7 @@ namespace ts.codefix { const info = getInfo(sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; const { node, suggestion } = info; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, node, suggestion)); const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_spelling_to_0), [suggestion]); return [{ description, changes, fixId }]; }, diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index b7d948b3deb..93c8ae80ac9 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -13,13 +13,13 @@ namespace ts.codefix { const token = getToken(sourceFile, context.span.start); const result: CodeFixAction[] = []; - const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(t, sourceFile, token)); + const deletion = textChanges.ChangeTracker.with(toTextChangesContext(context), t => tryDeleteDeclaration(t, sourceFile, token)); if (deletion.length) { const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), [token.getText()]); result.push({ description, changes: deletion, fixId: fixIdDelete }); } - const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, context.errorCode, sourceFile, token)); + const prefix = textChanges.ChangeTracker.with(toTextChangesContext(context), t => tryPrefixDeclaration(t, context.errorCode, sourceFile, token)); if (prefix.length) { const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Prefix_0_with_an_underscore), [token.getText()]); result.push({ description, changes: prefix, fixId: fixIdPrefix }); diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 8f69cd95c11..204030e3104 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -24,16 +24,14 @@ namespace ts.codefix { moduleSpecifier?: string; } - interface SymbolContext extends textChanges.TextChangesContext { + interface SymbolContext { sourceFile: SourceFile; symbolName: string; + formatContext: ts.formatting.FormatContext; } - interface SymbolAndTokenContext extends SymbolContext { + interface ImportCodeFixContext extends SymbolContext { symbolToken: Identifier | undefined; - } - - interface ImportCodeFixContext extends SymbolAndTokenContext { host: LanguageServiceHost; program: Program; checker: TypeChecker; @@ -173,7 +171,6 @@ namespace ts.codefix { const symbolToken = cast(getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false), isIdentifier); return { host: context.host, - newLineCharacter: context.newLineCharacter, formatContext: context.formatContext, sourceFile: context.sourceFile, program, @@ -260,7 +257,7 @@ namespace ts.codefix { } } - function getCodeActionForNewImport(context: SymbolContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { + function getCodeActionForNewImport(context: SymbolContext & RefactorOrCodeFixContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { const { kind, sourceFile, symbolName } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); @@ -278,7 +275,7 @@ namespace ts.codefix { createIdentifier(symbolName), createExternalModuleReference(quotedModuleSpecifier)); - const changes = ChangeTracker.with(context, changeTracker => { + const changes = ChangeTracker.with(toTextChangesContext(context), changeTracker => { if (lastImportDeclaration) { changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl); } @@ -672,33 +669,33 @@ namespace ts.codefix { return expression && isStringLiteral(expression) ? expression.text : undefined; } - function tryUpdateExistingImport(context: SymbolContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { + function tryUpdateExistingImport(context: SymbolContext & RefactorOrCodeFixContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { const { symbolName, sourceFile, kind } = context; const { name } = importClause; const { namedBindings } = importClause.kind !== SyntaxKind.ImportEqualsDeclaration && importClause; switch (kind) { case ImportKind.Default: - return name ? undefined : ChangeTracker.with(context, t => + return name ? undefined : ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(sourceFile, importClause, createImportClause(createIdentifier(symbolName), namedBindings))); case ImportKind.Named: { const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(symbolName)); if (namedBindings && namedBindings.kind === SyntaxKind.NamedImports && namedBindings.elements.length !== 0) { // There are already named imports; add another. - return ChangeTracker.with(context, t => t.insertNodeInListAfter( + return ChangeTracker.with(toTextChangesContext(context), t => t.insertNodeInListAfter( sourceFile, namedBindings.elements[namedBindings.elements.length - 1], newImportSpecifier)); } if (!namedBindings || namedBindings.kind === SyntaxKind.NamedImports && namedBindings.elements.length === 0) { - return ChangeTracker.with(context, t => + return ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(sourceFile, importClause, createImportClause(name, createNamedImports([newImportSpecifier])))); } return undefined; } case ImportKind.Namespace: - return namedBindings ? undefined : ChangeTracker.with(context, t => + return namedBindings ? undefined : ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(sourceFile, importClause, createImportClause(name, createNamespaceImport(createIdentifier(symbolName))))); case ImportKind.Equals: @@ -709,7 +706,7 @@ namespace ts.codefix { } } - function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext, symbolToken: Identifier): ImportCodeAction { + function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext & RefactorOrCodeFixContext, symbolToken: Identifier): ImportCodeAction { const { symbolName, sourceFile } = context; /** @@ -723,7 +720,7 @@ namespace ts.codefix { * become "ns.foo" */ // Prefix the node instead of it replacing it, because this may be used for import completions and we don't want the text changes to overlap with the identifier being completed. - const changes = ChangeTracker.with(context, tracker => + const changes = ChangeTracker.with(toTextChangesContext(context), tracker => tracker.changeIdentifierToPropertyAccess(sourceFile, namespacePrefix, symbolToken)); return createCodeAction(Diagnostics.Change_0_to_1, [symbolName, `${namespacePrefix}.${symbolName}`], changes, "CodeChange", /*moduleSpecifier*/ undefined); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 36a10a1b578..82e7065228a 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -627,7 +627,6 @@ namespace ts.Completions { host, program, checker, - newLineCharacter: host.getNewLine(), compilerOptions, sourceFile, formatContext, diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 85ef9113bda..3d2a60e63e9 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -14,12 +14,28 @@ namespace ts { getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined; } - export interface RefactorContext extends textChanges.TextChangesContext { + export interface RefactorOrCodeFixContext { + host: LanguageServiceHost; + formatContext: ts.formatting.FormatContext; + } + + export function getNewLineFromContext(context: RefactorOrCodeFixContext) { + const formatSettings = context.formatContext.options; + return formatSettings ? formatSettings.newLineCharacter : context.host.getNewLine(); + } + + export function toTextChangesContext(context: RefactorOrCodeFixContext): textChanges.TextChangesContext { + return { + newLineCharacter: getNewLineFromContext(context), + formatContext: context.formatContext, + }; + } + + export interface RefactorContext extends RefactorOrCodeFixContext { file: SourceFile; startPosition: number; endPosition?: number; program: Program; - host: LanguageServiceHost; cancellationToken?: CancellationToken; } diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts index d3bf59638b2..a39cdc20c42 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -78,7 +78,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { return Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`); } - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode); suppressLeadingAndTrailingTrivia(declarationWithType); changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, declarationWithType); @@ -93,7 +93,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { const sourceFile = context.file; const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false); const decl = findAncestor(token, isFunctionLikeDeclaration); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); const functionWithType = addTypesToFunctionLike(decl); suppressLeadingAndTrailingTrivia(functionWithType); changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, functionWithType); diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index cddf40ae017..93e39bc683c 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -59,7 +59,7 @@ namespace ts.refactor.convertFunctionToES6Class { } const ctorDeclaration = ctorSymbol.valueDeclaration; - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); let precedingNode: Node; let newClassDeclaration: ClassDeclaration; diff --git a/src/services/refactors/convertToEs6Module.ts b/src/services/refactors/convertToEs6Module.ts index 1046bf90aa6..933797554dd 100644 --- a/src/services/refactors/convertToEs6Module.ts +++ b/src/services/refactors/convertToEs6Module.ts @@ -74,7 +74,7 @@ namespace ts.refactor { Debug.assertEqual(actionName, _actionName); const { file, program } = context; Debug.assert(isSourceFileJavaScript(file)); - const edits = textChanges.ChangeTracker.with(context, changes => { + const edits = textChanges.ChangeTracker.with(toTextChangesContext(context), changes => { const moduleExportsChangedToDefault = convertFileToEs6Module(file, program.getTypeChecker(), changes, program.getCompilerOptions().target); if (moduleExportsChangedToDefault) { for (const importingFile of program.getSourceFiles()) { diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index b3110a0ca36..9a9c30756a4 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -806,7 +806,7 @@ namespace ts.refactor.extractSymbol { ); } - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end; const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { @@ -1011,7 +1011,7 @@ namespace ts.refactor.extractSymbol { const initializer = transformConstantInitializer(node, substitutions); suppressLeadingAndTrailingTrivia(initializer); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); if (isClassLike(scope)) { Debug.assert(!isJS); // See CannotExtractToJSClass diff --git a/src/services/refactors/useDefaultImport.ts b/src/services/refactors/useDefaultImport.ts index a103168f67b..e64eaf0bfcb 100644 --- a/src/services/refactors/useDefaultImport.ts +++ b/src/services/refactors/useDefaultImport.ts @@ -54,7 +54,7 @@ namespace ts.refactor.installTypesForPackage { const newImportClause = createImportClause(name, /*namedBindings*/ undefined); const newImportStatement = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, newImportClause, moduleSpecifier); return { - edits: textChanges.ChangeTracker.with(context, t => t.replaceNode(file, importStatement, newImportStatement)), + edits: textChanges.ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(file, importStatement, newImportStatement)), renameFilename: undefined, renameLocation: undefined, }; diff --git a/src/services/services.ts b/src/services/services.ts index 4236416fbb3..1692da9872b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1887,12 +1887,11 @@ namespace ts { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); - const newLineCharacter = getNewLineOrDefaultFromHost(host); const formatContext = formatting.getFormatContext(formatOptions); return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { cancellationToken.throwIfCancellationRequested(); - return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, formatContext }); + return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext }); }); } @@ -1900,10 +1899,9 @@ namespace ts { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); - const newLineCharacter = getNewLineOrDefaultFromHost(host); const formatContext = formatting.getFormatContext(formatOptions); - return codefix.getAllFixes({ fixId, sourceFile, program, newLineCharacter, host, cancellationToken, formatContext }); + return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext }); } function applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -2134,7 +2132,6 @@ namespace ts { startPosition, endPosition, program: getProgram(), - newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(), host, formatContext: formatting.getFormatContext(formatOptions), cancellationToken,