Skip to content

Commit

Permalink
feat(37409): add fix all quick fix to fixInvalidJsxCharacters
Browse files Browse the repository at this point in the history
  • Loading branch information
a-tarasyuk committed Mar 17, 2020
1 parent 9120497 commit 8ed7491
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 58 deletions.
10 changes: 9 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -5513,10 +5513,18 @@
"category": "Message",
"code": 95100
},
"Wrap invalid character in an expression container": {
"Convert all invalid characters to HTML entity code": {
"category": "Message",
"code": 95101
},
"Wrap invalid character in an expression container": {
"category": "Message",
"code": 95102
},
"Wrap all invalid characters in an expression container": {
"category": "Message",
"code": 95103
},

"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
Expand Down
6 changes: 5 additions & 1 deletion src/services/codeFixProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ namespace ts.codefix {
}

function getDiagnostics({ program, sourceFile, cancellationToken }: CodeFixContextBase) {
return program.getSemanticDiagnostics(sourceFile, cancellationToken).concat(computeSuggestionDiagnostics(sourceFile, program, cancellationToken));
return [
...program.getSemanticDiagnostics(sourceFile, cancellationToken),
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
...computeSuggestionDiagnostics(sourceFile, program, cancellationToken)
];
}
}
34 changes: 20 additions & 14 deletions src/services/codefixes/fixInvalidJsxCharacters.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
/* @internal */
namespace ts.codefix {
const fixIdHtmlEntity = "invalidJsxCharactersConvertToHtmlEntity";
const fixIdExpression = "invalidJsxCharactersConvertToExpression";
const fixIdExpression = "fixInvalidJsxCharacters_expression";
const fixIdHtmlEntity = "fixInvalidJsxCharacters_htmlEntity";

const errorCodes = [Diagnostics.Unexpected_token_Did_you_mean_or_gt.code, Diagnostics.Unexpected_token_Did_you_mean_or_rbrace.code];
const errorCodes = [
Diagnostics.Unexpected_token_Did_you_mean_or_gt.code,
Diagnostics.Unexpected_token_Did_you_mean_or_rbrace.code
];

registerCodeFix({
errorCodes,
getCodeActions: context => {
const { sourceFile, span } = context;
const changeToExpression = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, span.start, /* useHtmlEntity */ false));
const changeToHtmlEntity = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, span.start, /* useHtmlEntity */ true));
fixIds: [fixIdExpression, fixIdHtmlEntity],
getCodeActions(context) {
const { sourceFile, preferences, span } = context;
const changeToExpression = textChanges.ChangeTracker.with(context, t => doChange(t, preferences, sourceFile, span.start, /* useHtmlEntity */ false));
const changeToHtmlEntity = textChanges.ChangeTracker.with(context, t => doChange(t, preferences, sourceFile, span.start, /* useHtmlEntity */ true));

return [
createCodeFixActionWithoutFixAll(fixIdExpression, changeToExpression, Diagnostics.Wrap_invalid_character_in_an_expression_container),
createCodeFixAction(fixIdHtmlEntity, changeToHtmlEntity, Diagnostics.Convert_invalid_character_to_its_html_entity_code, fixIdHtmlEntity, Diagnostics.Convert_invalid_character_to_its_html_entity_code),
createCodeFixAction(fixIdExpression, changeToExpression, Diagnostics.Wrap_invalid_character_in_an_expression_container, fixIdExpression, Diagnostics.Wrap_all_invalid_characters_in_an_expression_container),
createCodeFixAction(fixIdHtmlEntity, changeToHtmlEntity, Diagnostics.Convert_invalid_character_to_its_html_entity_code, fixIdHtmlEntity, Diagnostics.Convert_all_invalid_characters_to_HTML_entity_code)
];
},
fixIds: [fixIdExpression, fixIdHtmlEntity],
getAllCodeActions(context) {
return codeFixAll(context, errorCodes, (changes, diagnostic) => doChange(changes, context.preferences, diagnostic.file, diagnostic.start, context.fixId === fixIdHtmlEntity));
}
});

const htmlEntity = {
">": ">",
"}": "}",
};

function isValidCharacter(character: string): character is keyof typeof htmlEntity {
return hasProperty(htmlEntity, character);
}

function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number, useHtmlEntity: boolean) {
function doChange(changes: textChanges.ChangeTracker, preferences: UserPreferences, sourceFile: SourceFile, start: number, useHtmlEntity: boolean) {
const character = sourceFile.getText()[start];
// sanity check
if (!isValidCharacter(character)) {
return;
}

const replacement = useHtmlEntity
? htmlEntity[character]
: `{'${character}'}`;
const replacement = useHtmlEntity ? htmlEntity[character] : `{${quote(character, preferences)}}`;
changes.replaceRangeWithText(sourceFile, { pos: start, end: start + 1 }, replacement);
}
}
13 changes: 6 additions & 7 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
// @jsx: react
// @filename: main.tsx

//// let x1 = <div>}</div>;
//// let foo = <div>}</div>;

verify.codeFix({
description: "Wrap invalid character in an expression container",
newFileContent:
`let x1 = <div>{'}'}</div>;`,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{"}"}</div>;`,
index: 0,
});

verify.codeFix({
description: "Convert invalid character to its html entity code",
newFileContent:
`let x1 = <div>&rbrace;</div>;`,
description: ts.Diagnostics.Convert_invalid_character_to_its_html_entity_code.message,
newFileContent: `let foo = <div>&rbrace;</div>;`,
index: 1,
});
13 changes: 13 additions & 0 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters10.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />

// @jsx: react
// @filename: main.tsx

//// let foo = <div>></div>;

verify.codeFix({
index: 0,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{">"}</div>;`,
preferences: { quotePreference: "double" }
});
13 changes: 6 additions & 7 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
// @jsx: react
// @filename: main.tsx

//// let x2 = <div>></div>;
//// let foo = <div>></div>;

verify.codeFix({
description: "Wrap invalid character in an expression container",
newFileContent:
`let x2 = <div>{'>'}</div>;`,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{">"}</div>;`,
index: 0,
});

verify.codeFix({
description: "Convert invalid character to its html entity code",
newFileContent:
`let x2 = <div>&gt;</div>;`,
description: ts.Diagnostics.Convert_invalid_character_to_its_html_entity_code.message,
newFileContent: `let foo = <div>&gt;</div>;`,
index: 1,
});
13 changes: 6 additions & 7 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
// @jsx: react
// @filename: main.tsx

//// let x3 = <div>{"foo"}}</div>;
//// let foo = <div>{"foo"}}</div>;

verify.codeFix({
description: "Wrap invalid character in an expression container",
newFileContent:
`let x3 = <div>{"foo"}{'}'}</div>;`,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{"foo"}{"}"}</div>;`,
index: 0,
});

verify.codeFix({
description: "Convert invalid character to its html entity code",
newFileContent:
`let x3 = <div>{"foo"}&rbrace;</div>;`,
description: ts.Diagnostics.Convert_invalid_character_to_its_html_entity_code.message,
newFileContent: `let foo = <div>{"foo"}&rbrace;</div>;`,
index: 1,
});
13 changes: 6 additions & 7 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
// @jsx: react
// @filename: main.tsx

//// let x4 = <div>{"foo"}></div>;
//// let foo = <div>{"foo"}></div>;

verify.codeFix({
description: "Wrap invalid character in an expression container",
newFileContent:
`let x4 = <div>{"foo"}{'>'}</div>;`,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{"foo"}{">"}</div>;`,
index: 0,
});

verify.codeFix({
description: "Convert invalid character to its html entity code",
newFileContent:
`let x4 = <div>{"foo"}&gt;</div>;`,
description: ts.Diagnostics.Convert_invalid_character_to_its_html_entity_code.message,
newFileContent: `let foo = <div>{"foo"}&gt;</div>;`,
index: 1,
});
13 changes: 6 additions & 7 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
// @jsx: react
// @filename: main.tsx

//// let x5 = <div>}{"foo"}</div>;
//// let foo = <div>}{"foo"}</div>;

verify.codeFix({
description: "Wrap invalid character in an expression container",
newFileContent:
`let x5 = <div>{'}'}{"foo"}</div>;`,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{"}"}{"foo"}</div>;`,
index: 0,
});

verify.codeFix({
description: "Convert invalid character to its html entity code",
newFileContent:
`let x5 = <div>&rbrace;{"foo"}</div>;`,
description: ts.Diagnostics.Convert_invalid_character_to_its_html_entity_code.message,
newFileContent: `let foo = <div>&rbrace;{"foo"}</div>;`,
index: 1,
});
13 changes: 6 additions & 7 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters6.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
// @jsx: react
// @filename: main.tsx

//// let x6 = <div>>{"foo"}</div>;
//// let foo = <div>>{"foo"}</div>;

verify.codeFix({
description: "Wrap invalid character in an expression container",
newFileContent:
`let x6 = <div>{'>'}{"foo"}</div>;`,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{">"}{"foo"}</div>;`,
index: 0,
});

verify.codeFix({
description: "Convert invalid character to its html entity code",
newFileContent:
`let x6 = <div>&gt;{"foo"}</div>;`,
description: ts.Diagnostics.Convert_invalid_character_to_its_html_entity_code.message,
newFileContent: `let foo = <div>&gt;{"foo"}</div>;`,
index: 1,
});
17 changes: 17 additions & 0 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters7.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />

// @jsx: react
// @filename: main.tsx

////let a = <div>>{"foo"}</div>;
////let b = <div>>{"foo"}</div>;
////let c = <div>>{"foo"}</div>;

verify.codeFixAll({
fixId: "fixInvalidJsxCharacters_htmlEntity",
fixAllDescription: ts.Diagnostics.Convert_all_invalid_characters_to_HTML_entity_code.message,
newFileContent:
`let a = <div>&gt;{"foo"}</div>;
let b = <div>&gt;{"foo"}</div>;
let c = <div>&gt;{"foo"}</div>;`
});
17 changes: 17 additions & 0 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters8.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />

// @jsx: react
// @filename: main.tsx

////let a = <div>>{"foo"}</div>;
////let b = <div>>{"foo"}</div>;
////let c = <div>>{"foo"}</div>;

verify.codeFixAll({
fixId: "fixInvalidJsxCharacters_expression",
fixAllDescription: ts.Diagnostics.Wrap_all_invalid_characters_in_an_expression_container.message,
newFileContent:
`let a = <div>{">"}{"foo"}</div>;
let b = <div>{">"}{"foo"}</div>;
let c = <div>{">"}{"foo"}</div>;`
});
13 changes: 13 additions & 0 deletions tests/cases/fourslash/codeFixInvalidJsxCharacters9.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />

// @jsx: react
// @filename: main.tsx

//// let foo = <div>></div>;

verify.codeFix({
index: 0,
description: ts.Diagnostics.Wrap_invalid_character_in_an_expression_container.message,
newFileContent: `let foo = <div>{'>'}</div>;`,
preferences: { quotePreference: "single" }
});

0 comments on commit 8ed7491

Please sign in to comment.