Skip to content

Handle linebreaks consistently in code fixes and refactorings #21158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2568,9 +2568,7 @@ Actual: ${stringify(fullActual)}`);
const originalContent = scriptInfo.content;
for (const codeFix of codeFixes) {
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
let text = this.rangeText(ranges[0]);
// TODO:GH#18445 (remove this line to see errors in many `importNameCodeFix` tests)
text = text.replace(/\r\n/g, "\n");
const text = this.rangeText(ranges[0]);
actualTextArray.push(text);
scriptInfo.updateContent(originalContent);
}
Expand Down
2 changes: 0 additions & 2 deletions src/harness/unittests/extractTestHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 0 additions & 1 deletion src/services/codeFixProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace ts {
export interface CodeFixContextBase extends textChanges.TextChangesContext {
sourceFile: SourceFile;
program: Program;
host: LanguageServiceHost;
cancellationToken: CancellationToken;
}

Expand Down
6 changes: 4 additions & 2 deletions src/services/codefixes/disableJsDiagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = getNewLineOrDefaultFromHost(context.host, context.formatContext.options);

return [{
description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message),
changes: [createFileTextChanges(sourceFile.fileName, [getIgnoreCommentLocationForLocation(sourceFile, span.start, newLineCharacter)])],
Expand All @@ -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, getNewLineOrDefaultFromHost(context.host, context.formatContext.options)));
}
}),
});
Expand Down
6 changes: 3 additions & 3 deletions src/services/codefixes/fixAddMissingMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ 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));
return { description, changes, fixId };
Expand All @@ -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(
Expand All @@ -181,7 +181,7 @@ namespace ts.codefix {
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));
return { description, changes, fixId };
Expand Down
7 changes: 1 addition & 6 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ namespace ts.codefix {
symbolName: string;
}

interface SymbolAndTokenContext extends SymbolContext {
interface ImportCodeFixContext extends SymbolContext {
symbolToken: Identifier | undefined;
}

interface ImportCodeFixContext extends SymbolAndTokenContext {
host: LanguageServiceHost;
program: Program;
checker: TypeChecker;
compilerOptions: CompilerOptions;
Expand Down Expand Up @@ -173,7 +169,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,
Expand Down
1 change: 0 additions & 1 deletion src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,6 @@ namespace ts.Completions {
host,
program,
checker,
newLineCharacter: host.getNewLine(),
compilerOptions,
sourceFile,
formatContext,
Expand Down
1 change: 0 additions & 1 deletion src/services/refactorProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace ts {
startPosition: number;
endPosition?: number;
program: Program;
host: LanguageServiceHost;
cancellationToken?: CancellationToken;
}

Expand Down
7 changes: 2 additions & 5 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1887,23 +1887,21 @@ 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 });
});
}

function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings): CombinedCodeActions {
synchronizeHostData();
Debug.assert(scope.type === "file");
const sourceFile = getValidSourceFile(scope.fileName);
const newLineCharacter = getNewLineOrDefaultFromHost(host);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should mark this function as deprecated, and replace all its uses as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears to be internal. Can we just delete it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the remaining callers look pretty legitimate. Is there another method they should call instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removign it should be fine then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. This method appears to have callers. Why are we deprecating/removing it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at this again, seems like in every call to this method, we have an insufficient public API. for instance, in getDocCommentTemplateAtPosition, seems like the public API should take a FormatCodeSettings just like getCodeFixesAtPosition does.
I do not think this is related to this PR per se, but this is something that we should fix for the long term. unfortunately we can not remove the getNewLineOrDefaultFromHost as you noted without breaking our public API, but we should add optional FormatCodeSettings in places where we use it to allow the host to override the value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mhegazy Can you please confirm that the latest commit (d97dec8) is what you had in mind (not counting a bug about reviewing callers of getNewLineOrDefaultFromHost)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Filed #21289.

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<ApplyCodeActionCommandResult>;
Expand Down Expand Up @@ -2134,7 +2132,6 @@ namespace ts {
startPosition,
endPosition,
program: getProgram(),
newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(),
host,
formatContext: formatting.getFormatContext(formatOptions),
cancellationToken,
Expand Down
4 changes: 2 additions & 2 deletions src/services/textChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ namespace ts.textChanges {
}

export interface TextChangesContext {
newLineCharacter: string;
host: LanguageServiceHost;
formatContext: ts.formatting.FormatContext;
}

Expand All @@ -199,7 +199,7 @@ namespace ts.textChanges {
private readonly nodesInsertedAtClassStarts = createMap<{ sourceFile: SourceFile, cls: ClassLikeDeclaration, members: ClassElement[] }>();

public static fromContext(context: TextChangesContext): ChangeTracker {
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext);
return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options) === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext);
}

public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] {
Expand Down
6 changes: 4 additions & 2 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1259,8 +1259,10 @@ namespace ts {
/**
* The default is CRLF.
*/
export function getNewLineOrDefaultFromHost(host: LanguageServiceHost | LanguageServiceShimHost) {
return host.getNewLine ? host.getNewLine() : carriageReturnLineFeed;
export function getNewLineOrDefaultFromHost(host: LanguageServiceHost | LanguageServiceShimHost, formatSettings?: FormatCodeSettings) {
return (formatSettings && formatSettings.newLineCharacter) ||
(host.getNewLine && host.getNewLine()) ||
carriageReturnLineFeed;
}

export function lineBreakPart() {
Expand Down
12 changes: 6 additions & 6 deletions tests/cases/fourslash/autoFormattingOnPasting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ public testMethod( )
}`);
// We're missing scenarios of formatting option settings due to bug 693273 - [TypeScript] Need to improve fourslash support for formatting options.
// Missing scenario ** Uncheck Tools->Options->Text Editor->TypeScript->Formatting->General->Format on paste **
//verify.currentFileContentIs("module TestModule {\r\n\
// class TestClass{\r\n\
//private foo;\r\n\
//public testMethod( )\r\n\
//{}\r\n\
//}\r\n\
//verify.currentFileContentIs("module TestModule {\n\
// class TestClass{\n\
//private foo;\n\
//public testMethod( )\n\
//{}\n\
//}\n\
//}");
// Missing scenario ** Check Tools->Options->Text Editor->TypeScript->Formatting->General->Format on paste **
verify.currentFileContentIs(`module TestModule {
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/fourslash/classInterfaceInsert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
verify.quickInfoAt("className", "class Sphere");

goTo.marker('interfaceGoesHere');
edit.insert("\r\ninterface Surface {\r\n reflect: () => number;\r\n}\r\n");
edit.insert("\ninterface Surface {\n reflect: () => number;\n}\n");

verify.quickInfoAt("className", "class Sphere");
3 changes: 1 addition & 2 deletions tests/cases/fourslash/codeFixAddMissingMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
verify.codeFix({
description: "Declare property 'foo'",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
foo: number;\r
foo: number;
method() {
this.foo = 10;
}
Expand Down
3 changes: 1 addition & 2 deletions tests/cases/fourslash/codeFixAddMissingMember2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
verify.codeFix({
description: "Add index signature for property 'foo'",
index: 1,
// TODO: GH#18445
newFileContent: `class C {
[x: string]: number;\r
[x: string]: number;
method() {
this.foo = 10;
}
Expand Down
3 changes: 1 addition & 2 deletions tests/cases/fourslash/codeFixAddMissingMember3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
verify.codeFix({
description: "Declare static property 'foo'",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
static foo: number;\r
static foo: number;
static method() {
this.foo = 10;
}
Expand Down
5 changes: 2 additions & 3 deletions tests/cases/fourslash/codeFixAddMissingMember4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
verify.codeFix({
description: "Initialize property 'foo' in the constructor",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
constructor() {\r
this.foo = undefined;\r
constructor() {
this.foo = undefined;
}
method() {
this.foo === 10;
Expand Down
5 changes: 2 additions & 3 deletions tests/cases/fourslash/codeFixAddMissingMember5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@
verify.codeFix({
description: "Initialize static property 'foo'",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
static method() {
()=>{ this.foo === 10 };
}
}\r
C.foo = undefined;\r
}
C.foo = undefined;
`
});
5 changes: 2 additions & 3 deletions tests/cases/fourslash/codeFixAddMissingMember6.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
verify.codeFix({
description: "Initialize property 'foo' in the constructor",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
constructor() {\r
this.foo = undefined;\r
constructor() {
this.foo = undefined;
}
prop = ()=>{ this.foo === 10 };
}`
Expand Down
5 changes: 2 additions & 3 deletions tests/cases/fourslash/codeFixAddMissingMember7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
verify.codeFix({
description: "Initialize static property 'foo'",
index: 2,
// TODO: GH#18445
newFileContent: `class C {
static p = ()=>{ this.foo === 10 };
}\r
C.foo = undefined;\r
}
C.foo = undefined;
`
});
9 changes: 4 additions & 5 deletions tests/cases/fourslash/codeFixAddMissingMember_all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
verify.codeFixAll({
fixId: "addMissingMember",
newFileContent:
// TODO: GH#18445
`class C {
x: number;\r
y(): any {\r
throw new Error("Method not implemented.");\r
}\r
x: number;
y(): any {
throw new Error("Method not implemented.");
}
method() {
this.x = 0;
this.y();
Expand Down
11 changes: 5 additions & 6 deletions tests/cases/fourslash/codeFixAddMissingMember_all_js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
verify.codeFixAll({
fixId: "addMissingMember",
newFileContent:
// TODO: GH#18445
`class C {
y() {\r
throw new Error("Method not implemented.");\r
}\r
constructor() {\r
this.x = undefined;\r
y() {
throw new Error("Method not implemented.");
}
constructor() {
this.x = undefined;
}
method() {
this.x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ verify.codeFix({
`class A {
f() {}
}
let B = class implements A {\r
f(): void {\r
throw new Error("Method not implemented.");\r
}\r
let B = class implements A {
f(): void {
throw new Error("Method not implemented.");
}
}`
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ verify.codeFix({
return C;
}

let B = class extends foo("s")<number> {\r
a: string | number;\r
let B = class extends foo("s")<number> {
a: string | number;
}`
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ verify.codeFix({
return C;
}

class B extends foo("s")<number> {\r
a: string | number;\r
class B extends foo("s")<number> {
a: string | number;
}`
});
16 changes: 8 additions & 8 deletions tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ verify.codeFix({
// Don't need to add anything in this case.
abstract class B extends A {}

class C extends A {\r
a: string | number;\r
b: this;\r
c: A;\r
d: string | number;\r
e: this;\r
f: A;\r
g: string;\r
class C extends A {
a: string | number;
b: this;
c: A;
d: string | number;
e: this;
f: A;
g: string;
}`
});
Loading