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
48export 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}
0 commit comments