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' ;
3
7
4
8
export function create ( ) : LanguageServicePlugin {
5
9
const base = baseCreate ( { scssDocumentSelector : [ 'scss' , 'postcss' ] } ) ;
6
10
return {
7
11
...base ,
8
12
create ( context ) {
9
13
const baseInstance = base . create ( context ) ;
14
+ const {
15
+ 'css/languageService' : getCssLs ,
16
+ 'css/stylesheet' : getStylesheet ,
17
+ } = baseInstance . provide as Provide ;
18
+
10
19
return {
11
20
...baseInstance ,
12
21
async provideDiagnostics ( document , token ) {
@@ -18,7 +27,73 @@ export function create(): LanguageServicePlugin {
18
27
}
19
28
return diagnostics ;
20
29
} ,
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
+ }
21
88
} ;
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
+ }
22
97
} ,
23
98
} ;
24
99
}
0 commit comments