Skip to content

Commit 21a1881

Browse files
authored
Avoid LSP request conflicts with Request Forwarding (#190)
* update launch.json * use request forwarding * fix warnings * add changelog * update comment
1 parent a1d3b1b commit 21a1881

File tree

3 files changed

+80
-14
lines changed

3 files changed

+80
-14
lines changed

.changeset/thick-pens-rule.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'css-modules-kit-vscode': patch
3+
---
4+
5+
fix: avoid LSP request conflicts with Request Forwarding

.vscode/launch.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"args": [
1919
"--extensionDevelopmentPath=${workspaceFolder}/packages/vscode",
2020
"--profile-temp",
21-
"--disable-extension=vscode.css-language-features",
2221
"--skip-welcome",
2322
"--folder-uri=${workspaceFolder}/example",
2423
"${workspaceFolder}/example/src/index.tsx"

packages/vscode/src/index.ts

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ import * as lsp from 'vscode-languageclient/node';
55

66
let client: lsp.BaseLanguageClient;
77

8-
export async function activate(_context: vscode.ExtensionContext) {
8+
const ORIGINAL_SCHEME = 'css-modules-kit';
9+
10+
type RangeOrRangeWithPlaceholder =
11+
| vscode.Range
12+
| {
13+
range: vscode.Range;
14+
placeholder: string;
15+
};
16+
17+
export async function activate(context: vscode.ExtensionContext) {
918
console.log('[css-modules-kit-vscode] Activated');
1019

1120
// By default, `vscode.typescript-language-features` is not activated when a user opens *.css in VS Code.
@@ -15,7 +24,6 @@ export async function activate(_context: vscode.ExtensionContext) {
1524
console.log('[css-modules-kit-vscode] Activating `vscode.typescript-language-features`');
1625
tsExtension.activate();
1726
}
18-
1927
// Both vscode.css-language-features extension and tsserver receive "rename" requests for *.css.
2028
// If more than one Provider receives a "rename" request, VS Code will use one of them.
2129
// In this case, the extension is used to rename. However, we do not want this.
@@ -29,17 +37,71 @@ export async function activate(_context: vscode.ExtensionContext) {
2937
// If not disabled, "rename" and "references" will behave in a way the user does not want.
3038
const cssExtension = vscode.extensions.getExtension('vscode.css-language-features');
3139
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-
// });
40+
// Both vscode.css-language-features and tsserver subscribe to LSP requests for .css and return responses.
41+
//
42+
// For requests like "completion" or "references" the merged results of the two responses are displayed
43+
// to the user. However, for certain types of requests like "rename" or "documentLink" the response
44+
// from one Language Server takes precedence. Which Language Server is prioritized is determined by scoring.
45+
//
46+
// - https://github.com/microsoft/vscode/issues/115354
47+
//
48+
// Limiting the discussion to vscode.css-language-features and tsserver, it seems that
49+
// vscode.css-language-features takes precedence. As a result, "rename" and "documentLink"
50+
// behave unexpectedly.
51+
//
52+
// - https://github.com/mizdra/css-modules-kit/issues/121
53+
// - Case 1 in https://github.com/mizdra/css-modules-kit/issues/133
54+
//
55+
// Therefore, in this case, we use the VS Code extension API to intercept "rename" and "documentLink"
56+
// requests and redirect them to tsserver. This technique is known as "Request Forwarding".
57+
//
58+
// - https://code.visualstudio.com/api/language-extensions/embedded-languages#request-forwarding
59+
//
60+
// Using Request Forwarding solves the problem of "rename" and "references" requests.
61+
vscode.workspace.registerTextDocumentContentProvider(ORIGINAL_SCHEME, {
62+
async provideTextDocumentContent(uri) {
63+
// `uri.fsPath` is in the format `/path/to/file.module.css.ts`.
64+
const actualFilePath = uri.fsPath.slice(0, -3); // Remove the '.ts' extension
65+
const actualFileDocument = await vscode.workspace.openTextDocument(actualFilePath);
66+
const text = actualFileDocument.getText();
67+
return text;
68+
},
69+
});
70+
context.subscriptions.push(
71+
vscode.languages.registerRenameProvider(
72+
{ language: 'css' },
73+
{
74+
provideRenameEdits(document, position, newName, _token) {
75+
// NOTE: Executing `executeDocumentRenameProvider` on a virtual text document causes a runtime error. This is probably a bug in vscode.
76+
return vscode.commands.executeCommand<vscode.WorkspaceEdit>(
77+
'vscode.executeDocumentRenameProvider',
78+
vscode.Uri.parse(`${ORIGINAL_SCHEME}://${document.fileName}.ts`),
79+
position,
80+
newName,
81+
);
82+
},
83+
prepareRename(document, position, _token) {
84+
return vscode.commands.executeCommand<RangeOrRangeWithPlaceholder>(
85+
'vscode.prepareRename',
86+
vscode.Uri.parse(`${ORIGINAL_SCHEME}://${document.fileName}.ts`),
87+
position,
88+
);
89+
},
90+
},
91+
),
92+
vscode.languages.registerDocumentLinkProvider(
93+
{ language: 'css' },
94+
{
95+
async provideDocumentLinks(document, _token) {
96+
// NOTE: Executing `executeDocumentLinkProvider` on a virtual text document, an empty array is always returned. This is probably a bug in vscode.
97+
return vscode.commands.executeCommand<vscode.Location[]>(
98+
'vscode.executeLinkProvider',
99+
vscode.Uri.parse(`${ORIGINAL_SCHEME}://${document.fileName}.ts`),
100+
);
101+
},
102+
},
103+
),
104+
);
43105
} else {
44106
// If vscode.css-language-features extension is disabled, start the customized language server for *.css, *.scss, and *.less.
45107
// The language server is based on the vscode-css-languageservice, but "rename" and "references" features are disabled.

0 commit comments

Comments
 (0)