Skip to content

Commit eadfcdd

Browse files
authored
fix(language-service): typescript-semantic renaming first in style blocks (vuejs#4685)
1 parent 66c50f5 commit eadfcdd

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

packages/language-service/lib/plugins/css.ts

+77-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
import type { LanguageServicePlugin } from '@volar/language-service';
2-
import { create as baseCreate } from 'volar-service-css';
1+
import type { LanguageServicePlugin, VirtualCode } from '@volar/language-service';
2+
import { VueVirtualCode } from '@vue/language-core';
3+
import { create as baseCreate, type Provide } from 'volar-service-css';
4+
import * as css from 'vscode-css-languageservice';
5+
import type { TextDocument } from 'vscode-languageserver-textdocument';
6+
import { URI } from 'vscode-uri';
37

48
export function create(): LanguageServicePlugin {
59
const base = baseCreate({ scssDocumentSelector: ['scss', 'postcss'] });
610
return {
711
...base,
812
create(context) {
913
const baseInstance = base.create(context);
14+
const {
15+
'css/languageService': getCssLs,
16+
'css/stylesheet': getStylesheet,
17+
} = baseInstance.provide as Provide;
18+
1019
return {
1120
...baseInstance,
1221
async provideDiagnostics(document, token) {
@@ -18,7 +27,73 @@ export function create(): LanguageServicePlugin {
1827
}
1928
return diagnostics;
2029
},
30+
/**
31+
* If the editing position is within the virtual code and navigation is enabled,
32+
* skip the CSS renaming feature.
33+
*/
34+
provideRenameRange(document, position) {
35+
do {
36+
const uri = URI.parse(document.uri);
37+
const decoded = context.decodeEmbeddedDocumentUri(uri);
38+
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
39+
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
40+
if (!sourceScript?.generated || !virtualCode?.id.startsWith('style_')) {
41+
break;
42+
}
43+
44+
const root = sourceScript.generated.root;
45+
if (!(root instanceof VueVirtualCode)) {
46+
break;
47+
}
48+
49+
const block = root.sfc.styles.find(style => style.name === decoded![1]);
50+
if (!block) {
51+
break;
52+
}
53+
54+
let script: VirtualCode | undefined;
55+
for (const [key, value] of sourceScript.generated.embeddedCodes) {
56+
if (key.startsWith('script_')) {
57+
script = value;
58+
break;
59+
}
60+
}
61+
if (!script) {
62+
break;
63+
}
64+
65+
const offset = document.offsetAt(position) + block.startTagEnd;
66+
for (const { sourceOffsets, lengths, data } of script.mappings) {
67+
if (
68+
!sourceOffsets.length
69+
|| !data.navigation
70+
|| typeof data.navigation === 'object' && !data.navigation.shouldRename
71+
) {
72+
continue;
73+
}
74+
75+
const start = sourceOffsets[0];
76+
const end = sourceOffsets.at(-1)! + lengths.at(-1)!;
77+
78+
if (offset >= start && offset <= end) {
79+
return;
80+
}
81+
}
82+
} while (0);
83+
84+
return worker(document, (stylesheet, cssLs) => {
85+
return cssLs.prepareRename(document, position, stylesheet);
86+
});
87+
}
2188
};
89+
90+
function worker<T>(document: TextDocument, callback: (stylesheet: css.Stylesheet, cssLs: css.LanguageService) => T) {
91+
const cssLs = getCssLs(document);
92+
if (!cssLs) {
93+
return;
94+
}
95+
return callback(getStylesheet(document, cssLs), cssLs);
96+
}
2297
},
2398
};
2499
}

packages/language-service/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"volar-service-pug-beautify": "0.0.62",
3535
"volar-service-typescript": "0.0.62",
3636
"volar-service-typescript-twoslash-queries": "0.0.62",
37+
"vscode-css-languageservice": "^6.3.1",
3738
"vscode-html-languageservice": "^5.2.0",
3839
"vscode-languageserver-textdocument": "^1.0.11",
3940
"vscode-uri": "^3.0.8"

pnpm-lock.yaml

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)