|
1 | 1 | /* eslint-disable no-console */ |
2 | 2 |
|
| 3 | +import type { |
| 4 | + CSSModulesKitRenameInfoRequest, |
| 5 | + CSSModulesKitRenameInfoResponse, |
| 6 | + CSSModulesKitRenameRequest, |
| 7 | + CSSModulesKitRenameResponse, |
| 8 | +} from '@css-modules-kit/ts-plugin/type'; |
3 | 9 | import * as vscode from 'vscode'; |
4 | | -import * as lsp from 'vscode-languageclient/node'; |
5 | 10 |
|
6 | | -let client: lsp.BaseLanguageClient; |
7 | | - |
8 | | -export async function activate(_context: vscode.ExtensionContext) { |
| 11 | +export async function activate(context: vscode.ExtensionContext) { |
9 | 12 | console.log('[css-modules-kit-vscode] Activated'); |
10 | 13 |
|
11 | 14 | // By default, `vscode.typescript-language-features` is not activated when a user opens *.css in VS Code. |
12 | 15 | // So, activate it manually. |
13 | 16 | const tsExtension = vscode.extensions.getExtension('vscode.typescript-language-features'); |
14 | | - if (tsExtension) { |
| 17 | + if (tsExtension && !tsExtension.isActive) { |
15 | 18 | console.log('[css-modules-kit-vscode] Activating `vscode.typescript-language-features`'); |
16 | | - tsExtension.activate(); |
| 19 | + await tsExtension.activate(); |
17 | 20 | } |
18 | 21 |
|
19 | | - // Both vscode.css-language-features extension and tsserver receive "rename" requests for *.css. |
20 | | - // If more than one Provider receives a "rename" request, VS Code will use one of them. |
21 | | - // In this case, the extension is used to rename. However, we do not want this. |
22 | | - // Without rename in tsserver, we cannot rename class selectors across *.css and *.ts. |
23 | | - // |
24 | | - // Also, VS Code seems to send "references" requests to both vscode.css-language-features extension |
25 | | - // and tsserver and merge the results of both. Thus, when a user executes "Find all references" |
26 | | - // on a class selector, the same class selector appears twice. |
27 | | - // |
28 | | - // To avoid this, we recommend disabling vscode.css-language-features extension. Disabling extensions is optional. |
29 | | - // If not disabled, "rename" and "references" will behave in a way the user does not want. |
30 | | - const cssExtension = vscode.extensions.getExtension('vscode.css-language-features'); |
31 | | - if (cssExtension) { |
32 | | - // Temporarily commented out |
33 | | - // vscode.window |
34 | | - // .showInformationMessage( |
35 | | - // '"Rename Symbol" and "Find All References" do not work in some cases because the "CSS Language Features" extension is enabled. Disabling the extension will make them work.', |
36 | | - // 'Show "CSS Language Features" extension', |
37 | | - // ) |
38 | | - // .then((selected) => { |
39 | | - // if (selected) { |
40 | | - // vscode.commands.executeCommand('workbench.extensions.search', '@builtin css-language-features'); |
41 | | - // } |
42 | | - // }); |
43 | | - } else { |
44 | | - // If vscode.css-language-features extension is disabled, start the customized language server for *.css, *.scss, and *.less. |
45 | | - // The language server is based on the vscode-css-languageservice, but "rename" and "references" features are disabled. |
46 | | - |
47 | | - // TODO: Do not use Node.js API |
48 | | - const serverModulePath = require.resolve('@css-modules-kit/language-server'); |
49 | | - |
50 | | - const serverOptions: lsp.ServerOptions = { |
51 | | - run: { |
52 | | - module: serverModulePath, |
53 | | - transport: lsp.TransportKind.ipc, |
54 | | - options: { execArgv: [] }, |
| 22 | + context.subscriptions.push( |
| 23 | + vscode.languages.registerRenameProvider( |
| 24 | + { scheme: 'file', language: 'css' }, |
| 25 | + { |
| 26 | + async provideRenameEdits(document, position, newName, _token) { |
| 27 | + const res = await vscode.commands.executeCommand<CSSModulesKitRenameResponse>( |
| 28 | + 'typescript.tsserverRequest', |
| 29 | + '_css-modules-kit:rename', |
| 30 | + { |
| 31 | + fileName: document.fileName, |
| 32 | + position: document.offsetAt(position), |
| 33 | + } satisfies CSSModulesKitRenameRequest['arguments'], |
| 34 | + ); |
| 35 | + if (!res.success || !res.body.result) return; |
| 36 | + const edit = new vscode.WorkspaceEdit(); |
| 37 | + for (const location of res.body.result) { |
| 38 | + // eslint-disable-next-line no-await-in-loop |
| 39 | + const document = await vscode.workspace.openTextDocument(location.fileName); |
| 40 | + const start = document.positionAt(location.textSpan.start); |
| 41 | + const end = document.positionAt(location.textSpan.start + location.textSpan.length); |
| 42 | + edit.replace(vscode.Uri.file(location.fileName), new vscode.Range(start, end), newName); |
| 43 | + } |
| 44 | + return edit; |
| 45 | + }, |
| 46 | + async prepareRename(document, position, _token) { |
| 47 | + const res = await vscode.commands.executeCommand<CSSModulesKitRenameInfoResponse>( |
| 48 | + 'typescript.tsserverRequest', |
| 49 | + '_css-modules-kit:renameInfo', |
| 50 | + { |
| 51 | + fileName: document.fileName, |
| 52 | + position: document.offsetAt(position), |
| 53 | + } satisfies CSSModulesKitRenameInfoRequest['arguments'], |
| 54 | + ); |
| 55 | + if (!res.success || !res.body.result.canRename) return; |
| 56 | + return new vscode.Range( |
| 57 | + document.positionAt(res.body.result.triggerSpan.start), |
| 58 | + document.positionAt(res.body.result.triggerSpan.start + res.body.result.triggerSpan.length), |
| 59 | + ); |
| 60 | + }, |
55 | 61 | }, |
56 | | - debug: { |
57 | | - module: serverModulePath, |
58 | | - transport: lsp.TransportKind.ipc, |
59 | | - options: { execArgv: ['--nolazy', `--inspect=${6009}`] }, |
| 62 | + ), |
| 63 | + vscode.languages.registerDocumentLinkProvider( |
| 64 | + { scheme: 'file', language: 'css' }, |
| 65 | + { |
| 66 | + provideDocumentLinks(document, _token) { |
| 67 | + // TODO |
| 68 | + return []; |
| 69 | + }, |
60 | 70 | }, |
61 | | - }; |
62 | | - const clientOptions: lsp.LanguageClientOptions = { |
63 | | - documentSelector: [{ language: 'css' }, { language: 'scss' }, { language: 'less' }], |
64 | | - initializationOptions: {}, |
65 | | - }; |
66 | | - client = new lsp.LanguageClient('css-modules-kit-vscode', 'css-modules-kit-vscode', serverOptions, clientOptions); |
67 | | - await client.start(); |
68 | | - } |
69 | | -} |
70 | | - |
71 | | -export function deactivate(): Thenable<unknown> | undefined { |
72 | | - return client?.stop(); |
| 71 | + ), |
| 72 | + ); |
73 | 73 | } |
0 commit comments