@@ -5,7 +5,16 @@ import * as lsp from 'vscode-languageclient/node';
5
5
6
6
let client : lsp . BaseLanguageClient ;
7
7
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 ) {
9
18
console . log ( '[css-modules-kit-vscode] Activated' ) ;
10
19
11
20
// 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) {
15
24
console . log ( '[css-modules-kit-vscode] Activating `vscode.typescript-language-features`' ) ;
16
25
tsExtension . activate ( ) ;
17
26
}
18
-
19
27
// Both vscode.css-language-features extension and tsserver receive "rename" requests for *.css.
20
28
// If more than one Provider receives a "rename" request, VS Code will use one of them.
21
29
// 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) {
29
37
// If not disabled, "rename" and "references" will behave in a way the user does not want.
30
38
const cssExtension = vscode . extensions . getExtension ( 'vscode.css-language-features' ) ;
31
39
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
+ ) ;
43
105
} else {
44
106
// If vscode.css-language-features extension is disabled, start the customized language server for *.css, *.scss, and *.less.
45
107
// The language server is based on the vscode-css-languageservice, but "rename" and "references" features are disabled.
0 commit comments