diff --git a/.changeset/four-papers-learn.md b/.changeset/four-papers-learn.md new file mode 100644 index 000000000..f5838bc91 --- /dev/null +++ b/.changeset/four-papers-learn.md @@ -0,0 +1,5 @@ +--- +'svelte-language-server': patch +--- + +feat: quick fix for adding lang="ts" diff --git a/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts b/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts index 693c2581a..d7624cdf5 100644 --- a/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CodeActionsProvider.ts @@ -59,6 +59,7 @@ import { isTextSpanInGeneratedCode, SnapshotMap } from './utils'; +import { Node } from 'vscode-html-languageservice'; /** * TODO change this to protocol constant if it's part of the protocol @@ -701,10 +702,8 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { ), ...this.getSvelteQuickFixes( lang, - document, cannotFindNameDiagnostic, tsDoc, - formatCodeBasis, userPreferences, formatCodeSettings ) @@ -760,8 +759,18 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { lang ); + const addLangCodeAction = this.getAddLangTSCodeAction( + document, + context, + tsDoc, + formatCodeBasis + ); + // filter out empty code action - return codeActionsNotFilteredOut.map(({ codeAction }) => codeAction).concat(fixAllActions); + const result = codeActionsNotFilteredOut + .map(({ codeAction }) => codeAction) + .concat(fixAllActions); + return addLangCodeAction ? [addLangCodeAction].concat(result) : result; } private async convertAndFixCodeFixAction({ @@ -1128,10 +1137,8 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { private getSvelteQuickFixes( lang: ts.LanguageService, - document: Document, cannotFindNameDiagnostics: Diagnostic[], tsDoc: DocumentSnapshot, - formatCodeBasis: FormatCodeBasis, userPreferences: ts.UserPreferences, formatCodeSettings: ts.FormatCodeSettings ): CustomFixCannotFindNameInfo[] { @@ -1141,14 +1148,10 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { return []; } - const typeChecker = program.getTypeChecker(); const results: CustomFixCannotFindNameInfo[] = []; - const quote = getQuotePreference(sourceFile, userPreferences); const getGlobalCompletion = memoize(() => lang.getCompletionsAtPosition(tsDoc.filePath, 0, userPreferences, formatCodeSettings) ); - const [tsMajorStr] = ts.version.split('.'); - const tsSupportHandlerQuickFix = parseInt(tsMajorStr) >= 5; for (const diagnostic of cannotFindNameDiagnostics) { const identifier = this.findIdentifierForDiagnostic(tsDoc, diagnostic, sourceFile); @@ -1173,24 +1176,6 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { ); } - if (!tsSupportHandlerQuickFix) { - const isQuickFixTargetEventHandler = this.isQuickFixForEventHandler( - document, - diagnostic - ); - if (isQuickFixTargetEventHandler) { - fixes.push( - ...this.getEventHandlerQuickFixes( - identifier, - tsDoc, - typeChecker, - quote, - formatCodeBasis - ) - ); - } - } - if (!fixes.length) { continue; } @@ -1225,8 +1210,6 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { return identifier; } - // TODO: Remove this in late 2023 - // when most users have upgraded to TS 5.0+ private getSvelteStoreQuickFixes( identifier: ts.Identifier, lang: ts.LanguageService, @@ -1275,101 +1258,121 @@ export class CodeActionsProviderImpl implements CodeActionsProvider { return flatten(completion.entries.filter((c) => c.name === storeIdentifier).map(toFix)); } - /** - * Workaround for TypeScript doesn't provide a quick fix if the signature is typed as union type, like `(() => void) | null` - * We can remove this once TypeScript doesn't have this limitation. - */ - private getEventHandlerQuickFixes( - identifier: ts.Identifier, + private getAddLangTSCodeAction( + document: Document, + context: CodeActionContext, tsDoc: DocumentSnapshot, - typeChecker: ts.TypeChecker, - quote: string, formatCodeBasis: FormatCodeBasis - ): ts.CodeFixAction[] { - const type = identifier && typeChecker.getContextualType(identifier); + ) { + if (tsDoc.scriptKind !== ts.ScriptKind.JS) { + return; + } - // if it's not union typescript should be able to do it. no need to enhance - if (!type || !type.isUnion()) { - return []; + let hasTSOnlyDiagnostic = false; + for (const diagnostic of context.diagnostics) { + const num = Number(diagnostic.code); + const canOnlyBeUsedInTS = num >= 8004 && num <= 8017; + if (canOnlyBeUsedInTS) { + hasTSOnlyDiagnostic = true; + break; + } + } + if (!hasTSOnlyDiagnostic) { + return; } - const nonNullable = type.getNonNullableType(); + if (!document.scriptInfo && !document.moduleScriptInfo) { + const hasNonTopLevelLang = document.html.roots.some((node) => + this.hasLangTsScriptTag(node) + ); + // Might be because issue with parsing the script tag, so don't suggest adding a new one + if (hasNonTopLevelLang) { + return; + } - if ( - !( - nonNullable.flags & ts.TypeFlags.Object && - (nonNullable as ts.ObjectType).objectFlags & ts.ObjectFlags.Anonymous - ) - ) { - return []; + return CodeAction.create( + 'Add ' + formatCodeBasis.newLine + } + ] + } + ] + }, + CodeActionKind.QuickFix + ); } - const signature = typeChecker.getSignaturesOfType(nonNullable, ts.SignatureKind.Call)[0]; + const edits = [document.scriptInfo, document.moduleScriptInfo] + .map((info) => { + if (!info) { + return; + } - const parameters = signature.parameters.map((p) => { - const declaration = p.valueDeclaration ?? p.declarations?.[0]; - const typeString = declaration - ? typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(p, declaration)) - : ''; + const startTagNameEnd = document.positionAt(info.container.start + 7); // \n', + range: { + start: { + character: 0, + line: 0 + }, + end: { + character: 0, + line: 0 + } + } + } + ], + textDocument: { + uri: getUri('codeaction-add-lang-ts-no-script.svelte'), + version: null + } + } + ] + } + } + ]); + }); }); diff --git a/packages/language-server/test/plugins/typescript/testfiles/code-actions/codeaction-add-lang-ts-no-script.svelte b/packages/language-server/test/plugins/typescript/testfiles/code-actions/codeaction-add-lang-ts-no-script.svelte new file mode 100644 index 000000000..3a462a520 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/code-actions/codeaction-add-lang-ts-no-script.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/language-server/test/plugins/typescript/testfiles/code-actions/codeaction-add-lang-ts.svelte b/packages/language-server/test/plugins/typescript/testfiles/code-actions/codeaction-add-lang-ts.svelte new file mode 100644 index 000000000..0c39d5de6 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/code-actions/codeaction-add-lang-ts.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json index 15b242f11..833c8e36a 100644 --- a/packages/svelte2tsx/package.json +++ b/packages/svelte2tsx/package.json @@ -50,7 +50,7 @@ "build": "rollup -c", "prepublishOnly": "npm run build", "dev": "rollup -c -w", - "test": "mocha test/test.ts" + "test": "mocha test/test.ts --no-experimental-strip-types" }, "files": [ "index.mjs",