Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CodeActionKind,
Diagnostic,
OptionalVersionedTextDocumentIdentifier,
Position,
Range,
TextDocumentEdit,
TextEdit,
Expand All @@ -15,6 +16,7 @@ import {
Document,
getLineAtPosition,
isAtEndOfLine,
isInTag,
isRangeInTag,
mapRangeToOriginal
} from '../../../lib/documents';
Expand Down Expand Up @@ -355,6 +357,14 @@ export class CodeActionsProviderImpl implements CodeActionsProvider {
);
}

if (fix.fixName === 'inferFromUsage') {
originalRange = this.checkAddJsDocCodeActionRange(
snapshot,
originalRange,
document
);
}

if (originalRange.start.line < 0 || originalRange.end.line < 0) {
return undefined;
}
Expand Down Expand Up @@ -666,18 +676,13 @@ export class CodeActionsProviderImpl implements CodeActionsProvider {
originalRange: Range,
document: Document,
edit: ts.TextChange
): TextEdit {
const startOffset = document.offsetAt(originalRange.start);
const text = document.getText();

// svetlte2tsx removes export in instance script
const insertedAfterExport = text.slice(0, startOffset).trim().endsWith('export');

if (!insertedAfterExport) {
return TextEdit.replace(originalRange, edit.newText);
): TextEdit | null {
if (!isInTag(originalRange.start, document.scriptInfo)) {
return null;
}

const position = document.positionAt(text.lastIndexOf('export', startOffset));
const position =
this.fixPropsCodeActionRange(originalRange.start, document) ?? originalRange.start;

// fix the length of trailing indent
const linesOfNewText = edit.newText.split('\n');
Expand All @@ -690,6 +695,56 @@ export class CodeActionsProviderImpl implements CodeActionsProvider {
return TextEdit.insert(position, linesOfNewText.join('\n'));
}

/**
* svelte2tsx removes export in instance script
*/
private fixPropsCodeActionRange(start: Position, document: Document): Position | undefined {
const documentText = document.getText();
const offset = document.offsetAt(start);
const exportKeywordOffset = documentText.lastIndexOf('export', offset);

// export let a;
if (
exportKeywordOffset < 0 ||
documentText.slice(exportKeywordOffset + 'export'.length, offset).trim()
) {
return;
}

const charBeforeExport = documentText[exportKeywordOffset - 1];
if (
(charBeforeExport !== undefined && !charBeforeExport.trim()) ||
charBeforeExport === ';'
) {
return document.positionAt(exportKeywordOffset);
}
}

private checkAddJsDocCodeActionRange(
snapshot: DocumentSnapshot,
originalRange: Range,
document: Document
): Range {
if (
snapshot.scriptKind !== ts.ScriptKind.JS &&
snapshot.scriptKind !== ts.ScriptKind.JSX &&
!isInTag(originalRange.start, document.scriptInfo)
) {
return originalRange;
}

const position = this.fixPropsCodeActionRange(originalRange.start, document);

if (position) {
return {
start: position,
end: position
};
}

return originalRange;
}

private async getLSAndTSDoc(document: Document) {
return this.lsAndTsDocResolver.getLSAndTSDoc(document);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,94 @@ function test(useNewTransformation: boolean) {
]);
});

it('provide quickfix for adding jsDoc type to props', async () => {
const { provider, document } = setup('codeaction-add-jsdoc.svelte');
const errorRange = Range.create(Position.create(7, 8), Position.create(7, 11));

const codeActions = await provider.getCodeActions(document, errorRange, {
diagnostics: [
{
code: 7034,
message:
"Variable 'abc' implicitly has type 'any' in some locations where its type cannot be determined.",
range: errorRange
}
]
});

const addJsDoc = codeActions.find(
(fix) => fix.title === "Infer type of 'abc' from usage"
);

(<TextDocumentEdit>addJsDoc?.edit?.documentChanges?.[0])?.edits.forEach(
(edit) => (edit.newText = harmonizeNewLines(edit.newText))
);

assert.deepStrictEqual(addJsDoc?.edit, {
documentChanges: [
<TextDocumentEdit>{
edits: [
{
newText: `/**\n${indent} * @type {any}\n${indent} */\n${indent}`,
range: {
start: { character: 4, line: 3 },
end: { character: 4, line: 3 }
}
}
],
textDocument: {
uri: getUri('codeaction-add-jsdoc.svelte'),
version: null
}
}
]
});
});

it('provide quickfix for adding jsDoc type to non props when props exist', async () => {
const { provider, document } = setup('codeaction-add-jsdoc.svelte');
const errorRange = Range.create(Position.create(9, 8), Position.create(9, 10));

const codeActions = await provider.getCodeActions(document, errorRange, {
diagnostics: [
{
code: 7034,
message:
"Variable 'ab' implicitly has type 'any' in some locations where its type cannot be determined.",
range: errorRange
}
]
});

const addJsDoc = codeActions.find(
(fix) => fix.title === "Infer type of 'ab' from usage"
);

(<TextDocumentEdit>addJsDoc?.edit?.documentChanges?.[0])?.edits.forEach(
(edit) => (edit.newText = harmonizeNewLines(edit.newText))
);

assert.deepStrictEqual(addJsDoc?.edit, {
documentChanges: [
<TextDocumentEdit>{
edits: [
{
newText: `/**\n${indent} * @type {any}\n${indent} */\n${indent}`,
range: {
start: { character: 4, line: 9 },
end: { character: 4, line: 9 }
}
}
],
textDocument: {
uri: getUri('codeaction-add-jsdoc.svelte'),
version: null
}
}
]
});
});

it('provides quickfix for component import', async () => {
const { provider, document } = setup('codeactions.svelte');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script>
// @ts-check

export



let abc;

let ab;
</script>

{abc}
{ab}