diff --git a/.changeset/long-dolphins-glow.md b/.changeset/long-dolphins-glow.md new file mode 100644 index 00000000..500859d1 --- /dev/null +++ b/.changeset/long-dolphins-glow.md @@ -0,0 +1,6 @@ +--- +"@astrojs/language-server": minor +"astro-vscode": minor +--- + +Allow disabling the handling of updating imports when files are renamed. This is now disabled by default in VS Code, as the Astro TypeScript plugin will handle it correctly. diff --git a/packages/language-server/src/plugins/typescript/index.ts b/packages/language-server/src/plugins/typescript/index.ts index 9d816755..7aeb5270 100644 --- a/packages/language-server/src/plugins/typescript/index.ts +++ b/packages/language-server/src/plugins/typescript/index.ts @@ -16,6 +16,18 @@ export const create = (ts: typeof import('typescript')): LanguageServicePlugin[] const typeScriptPlugin = plugin.create(context); return { ...typeScriptPlugin, + async provideFileRenameEdits(oldUri, newUri, token) { + const astroConfig = await context.env.getConfiguration?.<{ + updateImportsOnFileMove: { enabled: boolean }; + }>('astro'); + + // Check for `false` explicitly, as the default value is `true`, but it might not be set explicitly depending on the editor + if (astroConfig?.updateImportsOnFileMove.enabled === false) { + return null; + } + + return typeScriptPlugin.provideFileRenameEdits!(oldUri, newUri, token); + }, async provideCompletionItems(document, position, completionContext, token) { const originalCompletions = await typeScriptPlugin.provideCompletionItems!( document, diff --git a/packages/language-server/test/fixture/renameThis.ts b/packages/language-server/test/fixture/renameThis.ts new file mode 100644 index 00000000..d3ba19a5 --- /dev/null +++ b/packages/language-server/test/fixture/renameThis.ts @@ -0,0 +1,3 @@ +export function sayHello() { + console.log('Hello'); +} diff --git a/packages/language-server/test/fixture/renaming.astro b/packages/language-server/test/fixture/renaming.astro new file mode 100644 index 00000000..c3e96e10 --- /dev/null +++ b/packages/language-server/test/fixture/renaming.astro @@ -0,0 +1,3 @@ +--- +import { sayHello } from "./renameThis.js"; +--- diff --git a/packages/language-server/test/server.ts b/packages/language-server/test/server.ts index 7f99bcf9..7084ea77 100644 --- a/packages/language-server/test/server.ts +++ b/packages/language-server/test/server.ts @@ -46,6 +46,7 @@ export async function getLanguageServer(): Promise { workspace: { // Needed for tests that use didChangeWatchedFiles didChangeWatchedFiles: {}, + configuration: true, }, }, ); diff --git a/packages/language-server/test/typescript/renames.test.ts b/packages/language-server/test/typescript/renames.test.ts new file mode 100644 index 00000000..b034c88b --- /dev/null +++ b/packages/language-server/test/typescript/renames.test.ts @@ -0,0 +1,87 @@ +import path from 'node:path'; +import { expect } from 'chai'; +import type { RenameFilesParams } from 'vscode-languageserver-protocol'; +import { WillRenameFilesRequest } from 'vscode-languageserver-protocol'; +import { type LanguageServer, getLanguageServer } from '../server.js'; + +const fixtureDir = path.join(__dirname, '../fixture'); + +describe('TypeScript - Renaming', async () => { + let languageServer: LanguageServer; + + before(async () => (languageServer = await getLanguageServer())); + + it('Renames imports for files when setting is not set', async () => { + const documentToBeRenamed = await languageServer.handle.openTextDocument( + path.resolve(fixtureDir, 'renameThis.ts'), + 'typescript', + ); + + const newUri = documentToBeRenamed.uri.replace('renameThis.ts', 'renamed.ts'); + + const edits = await languageServer.handle.connection.sendRequest(WillRenameFilesRequest.type, { + files: [ + { + oldUri: documentToBeRenamed.uri, + newUri: newUri, + }, + ], + }); + + expect(edits).to.not.be.null; + }); + + it('Does not rename imports for files when setting is disabled', async () => { + await languageServer.handle.updateConfiguration({ + astro: { + updateImportsOnFileMove: { + enabled: false, + }, + }, + }); + + const documentToBeRenamed = await languageServer.handle.openTextDocument( + path.resolve(fixtureDir, 'renameThis.ts'), + 'typescript', + ); + const newUri = documentToBeRenamed.uri.replace('renameThis.ts', 'renamed.ts'); + + const edits = await languageServer.handle.connection.sendRequest(WillRenameFilesRequest.type, { + files: [ + { + oldUri: documentToBeRenamed.uri, + newUri: newUri, + }, + ], + } satisfies RenameFilesParams); + + expect(edits).to.be.null; + }); + + it('Renames imports for files when setting is enabled', async () => { + await languageServer.handle.updateConfiguration({ + astro: { + updateImportsOnFileMove: { + enabled: true, + }, + }, + }); + + const documentToBeRenamed = await languageServer.handle.openTextDocument( + path.resolve(fixtureDir, 'renameThis.ts'), + 'typescript', + ); + const newUri = documentToBeRenamed.uri.replace('renameThis.ts', 'renamed.ts'); + + const edits = await languageServer.handle.connection.sendRequest(WillRenameFilesRequest.type, { + files: [ + { + oldUri: documentToBeRenamed.uri, + newUri: newUri, + }, + ], + }); + + expect(edits).to.not.be.null; + }); +}); diff --git a/packages/vscode/package.json b/packages/vscode/package.json index 157ff3b1..6207293c 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -154,6 +154,12 @@ "type": "boolean", "default": false, "description": "Enable experimental support for content collection intellisense inside Markdown, MDX and Markdoc. Note that this require also enabling the feature in your Astro config (experimental.contentCollectionIntellisense) (Astro 4.14+)" + }, + "astro.updateImportsOnFileMove.enabled": { + "scope": "resource", + "type": "boolean", + "default": false, + "description": "Controls whether the extension updates imports when a file is moved to a new location. In most cases, you'll want to keep this disabled as TypeScript and the Astro TypeScript plugin already handles this for you. Having multiple tools updating imports at the same time can lead to corrupted files." } } },