@@ -19,7 +19,8 @@ import {
19
19
import type * as monaco from 'monaco-editor-core'
20
20
// @ts -expect-error
21
21
import * as worker from 'monaco-editor-core/esm/vs/editor/editor.worker'
22
- import { create as createTypeScriptPlugins } from 'volar-service-typescript'
22
+ import { create as createTypeScriptDirectiveCommentPlugin } from 'volar-service-typescript/lib/plugins/directiveComment'
23
+ import { create as createTypeScriptSemanticPlugin } from 'volar-service-typescript/lib/plugins/semantic'
23
24
import { URI } from 'vscode-uri'
24
25
import type { WorkerHost , WorkerMessage } from './env'
25
26
@@ -91,165 +92,7 @@ self.onmessage = async (msg: MessageEvent<WorkerMessage>) => {
91
92
...getDefaultCompilerOptions ( ) ,
92
93
...tsconfig . vueCompilerOptions ,
93
94
}
94
- const globalTypes = generateGlobalTypes ( vueCompilerOptions )
95
- const globalTypesPath =
96
- '/node_modules/' + getGlobalTypesFileName ( vueCompilerOptions )
97
- vueCompilerOptions . globalTypesPath = ( ) => globalTypesPath
98
- const { stat, readFile } = env . fs !
99
- const ctime = Date . now ( )
100
- env . fs ! . stat = async ( uri ) => {
101
- if ( uri . path === globalTypesPath ) {
102
- return {
103
- type : 1 ,
104
- ctime : ctime ,
105
- mtime : ctime ,
106
- size : globalTypes . length ,
107
- }
108
- }
109
- return stat ( uri )
110
- }
111
- env . fs ! . readFile = async ( uri ) => {
112
- if ( uri . path === globalTypesPath ) {
113
- return globalTypes
114
- }
115
- return readFile ( uri )
116
- }
117
-
118
- const vueLanguagePlugin = createVueLanguagePlugin (
119
- ts ,
120
- compilerOptions ,
121
- vueCompilerOptions ,
122
- asFileName ,
123
- )
124
- const ignoreVueServicePlugins = new Set ( [
125
- 'vue-extract-file' ,
126
- 'vue-document-drop' ,
127
- 'vue-document-highlights' ,
128
- 'typescript-semantic-tokens' ,
129
- // dedupe
130
- 'typescript-doc-comment-template' ,
131
- 'typescript-syntactic' ,
132
- ] )
133
- const vueServicePlugins = createVueLanguageServicePlugins ( ts , {
134
- getComponentDirectives ( fileName ) {
135
- return getComponentDirectives ( ts , getProgram ( ) , fileName )
136
- } ,
137
- getComponentEvents ( fileName , tag ) {
138
- return getComponentEvents ( ts , getProgram ( ) , fileName , tag )
139
- } ,
140
- getComponentNames ( fileName ) {
141
- return getComponentNames ( ts , getProgram ( ) , fileName )
142
- } ,
143
- getComponentProps ( fileName , tag ) {
144
- return getComponentProps ( ts , getProgram ( ) , fileName , tag )
145
- } ,
146
- getComponentSlots ( fileName ) {
147
- const { virtualCode } = getVirtualCode ( fileName )
148
- return getComponentSlots ( ts , getProgram ( ) , virtualCode )
149
- } ,
150
- getElementAttrs ( fileName , tag ) {
151
- return getElementAttrs ( ts , getProgram ( ) , fileName , tag )
152
- } ,
153
- getElementNames ( fileName ) {
154
- return getElementNames ( ts , getProgram ( ) , fileName )
155
- } ,
156
- getPropertiesAtLocation ( fileName , position ) {
157
- const { sourceScript, virtualCode } = getVirtualCode ( fileName )
158
- return getPropertiesAtLocation (
159
- ts ,
160
- languageService . context . language ,
161
- getProgram ( ) ,
162
- sourceScript ,
163
- virtualCode ,
164
- position ,
165
- false ,
166
- )
167
- } ,
168
- async getQuickInfoAtPosition ( fileName , position ) {
169
- const uri = asUri ( fileName )
170
- const sourceScript = languageService . context . language . scripts . get ( uri )
171
- if ( ! sourceScript ) {
172
- return
173
- }
174
- const hover = await languageService . getHover ( uri , position )
175
- let text = ''
176
- if ( typeof hover ?. contents === 'string' ) {
177
- text = hover . contents
178
- } else if ( Array . isArray ( hover ?. contents ) ) {
179
- text = hover . contents
180
- . map ( ( c ) => ( typeof c === 'string' ? c : c . value ) )
181
- . join ( '\n' )
182
- } else if ( hover ) {
183
- text = hover . contents . value
184
- }
185
- text = text . replace ( / ` ` ` t y p e s c r i p t / g, '' )
186
- text = text . replace ( / ` ` ` / g, '' )
187
- text = text . replace ( / - - - / g, '' )
188
- text = text . trim ( )
189
- while ( true ) {
190
- const newText = text . replace ( / \n \n / g, '\n' )
191
- if ( newText === text ) {
192
- break
193
- }
194
- text = newText
195
- }
196
- text = text . replace ( / \n / g, ' | ' )
197
- return text
198
- } ,
199
- collectExtractProps ( ) {
200
- throw new Error ( 'Not implemented' )
201
- } ,
202
- getImportPathForFile ( ) {
203
- throw new Error ( 'Not implemented' )
204
- } ,
205
- getDocumentHighlights ( ) {
206
- throw new Error ( 'Not implemented' )
207
- } ,
208
- getEncodedSemanticClassifications ( ) {
209
- throw new Error ( 'Not implemented' )
210
- } ,
211
- } ) . filter ( ( plugin ) => ! ignoreVueServicePlugins . has ( plugin . name ! ) )
212
-
213
- const tsServicePlugins = createTypeScriptPlugins ( ts )
214
- for ( let i = 0 ; i < tsServicePlugins . length ; i ++ ) {
215
- const plugin = tsServicePlugins [ i ]
216
- if ( plugin . name === 'typescript-semantic' ) {
217
- tsServicePlugins [ i ] = {
218
- ...plugin ,
219
- create ( context ) {
220
- const created = plugin . create ( context )
221
- const tsLs = created . provide [
222
- 'typescript/languageService'
223
- ] ( ) as import ( 'typescript' ) . LanguageService
224
- const proxy = createVueLanguageServiceProxy (
225
- ts ,
226
- new Proxy (
227
- { } ,
228
- {
229
- get ( _target , prop , receiver ) {
230
- return Reflect . get (
231
- languageService . context . language ,
232
- prop ,
233
- receiver ,
234
- )
235
- } ,
236
- } ,
237
- ) as unknown as Language ,
238
- tsLs ,
239
- vueCompilerOptions ,
240
- asUri ,
241
- )
242
- tsLs . getCompletionsAtPosition = proxy . getCompletionsAtPosition
243
- tsLs . getCompletionEntryDetails = proxy . getCompletionEntryDetails
244
- tsLs . getCodeFixesAtPosition = proxy . getCodeFixesAtPosition
245
- tsLs . getDefinitionAndBoundSpan = proxy . getDefinitionAndBoundSpan
246
- tsLs . getQuickInfoAtPosition = proxy . getQuickInfoAtPosition
247
- return created
248
- } ,
249
- }
250
- break
251
- }
252
- }
95
+ setupGlobalTypes ( vueCompilerOptions , env )
253
96
254
97
const workerService = createTypeScriptWorkerLanguageService ( {
255
98
typescript : ts ,
@@ -260,33 +103,199 @@ self.onmessage = async (msg: MessageEvent<WorkerMessage>) => {
260
103
asFileName,
261
104
asUri,
262
105
} ,
263
- languagePlugins : [ vueLanguagePlugin ] ,
264
- languageServicePlugins : [ ...tsServicePlugins , ...vueServicePlugins ] ,
106
+ languagePlugins : [
107
+ createVueLanguagePlugin (
108
+ ts ,
109
+ compilerOptions ,
110
+ vueCompilerOptions ,
111
+ asFileName ,
112
+ ) ,
113
+ ] ,
114
+ languageServicePlugins : [
115
+ ...getTsLanguageServicePlugins ( ) ,
116
+ ...getVueLanguageServicePlugins ( ) ,
117
+ ] ,
265
118
} )
266
- const languageService = ( workerService as any )
267
- . languageService as LanguageService
268
119
269
120
return workerService
270
121
271
- function getProgram ( ) {
272
- const tsService : import ( 'typescript' ) . LanguageService =
273
- languageService . context . inject ( 'typescript/languageService' )
274
- return tsService . getProgram ( ) !
122
+ function setupGlobalTypes (
123
+ options : VueCompilerOptions ,
124
+ env : LanguageServiceEnvironment ,
125
+ ) {
126
+ const globalTypes = generateGlobalTypes ( options )
127
+ const globalTypesPath =
128
+ '/node_modules/' + getGlobalTypesFileName ( options )
129
+ options . globalTypesPath = ( ) => globalTypesPath
130
+ const { stat, readFile } = env . fs !
131
+ const ctime = Date . now ( )
132
+ env . fs ! . stat = async ( uri ) => {
133
+ if ( uri . path === globalTypesPath ) {
134
+ return {
135
+ type : 1 ,
136
+ ctime : ctime ,
137
+ mtime : ctime ,
138
+ size : globalTypes . length ,
139
+ }
140
+ }
141
+ return stat ( uri )
142
+ }
143
+ env . fs ! . readFile = async ( uri ) => {
144
+ if ( uri . path === globalTypesPath ) {
145
+ return globalTypes
146
+ }
147
+ return readFile ( uri )
148
+ }
149
+ }
150
+
151
+ function getTsLanguageServicePlugins ( ) {
152
+ const semanticPlugin = createTypeScriptSemanticPlugin ( ts )
153
+ const { create } = semanticPlugin
154
+ semanticPlugin . create = ( context ) => {
155
+ const created = create ( context )
156
+ const ls = created . provide [
157
+ 'typescript/languageService'
158
+ ] ( ) as import ( 'typescript' ) . LanguageService
159
+ const proxy = createVueLanguageServiceProxy (
160
+ ts ,
161
+ new Proxy (
162
+ { } ,
163
+ {
164
+ get ( _target , prop , receiver ) {
165
+ return Reflect . get ( context . language , prop , receiver )
166
+ } ,
167
+ } ,
168
+ ) as unknown as Language ,
169
+ ls ,
170
+ vueCompilerOptions ,
171
+ asUri ,
172
+ )
173
+ ls . getCompletionsAtPosition = proxy . getCompletionsAtPosition
174
+ ls . getCompletionEntryDetails = proxy . getCompletionEntryDetails
175
+ ls . getCodeFixesAtPosition = proxy . getCodeFixesAtPosition
176
+ ls . getDefinitionAndBoundSpan = proxy . getDefinitionAndBoundSpan
177
+ ls . getQuickInfoAtPosition = proxy . getQuickInfoAtPosition
178
+ return created
179
+ }
180
+ return [ semanticPlugin , createTypeScriptDirectiveCommentPlugin ( ) ]
275
181
}
276
182
277
- function getVirtualCode ( fileName : string ) {
278
- const uri = asUri ( fileName )
279
- const sourceScript = languageService . context . language . scripts . get ( uri )
280
- if ( ! sourceScript ) {
281
- throw new Error ( 'No source script found for file: ' + fileName )
183
+ function getVueLanguageServicePlugins ( ) {
184
+ const plugins = createVueLanguageServicePlugins ( ts , {
185
+ getComponentDirectives ( fileName ) {
186
+ return getComponentDirectives ( ts , getProgram ( ) , fileName )
187
+ } ,
188
+ getComponentEvents ( fileName , tag ) {
189
+ return getComponentEvents ( ts , getProgram ( ) , fileName , tag )
190
+ } ,
191
+ getComponentNames ( fileName ) {
192
+ return getComponentNames ( ts , getProgram ( ) , fileName )
193
+ } ,
194
+ getComponentProps ( fileName , tag ) {
195
+ return getComponentProps ( ts , getProgram ( ) , fileName , tag )
196
+ } ,
197
+ getComponentSlots ( fileName ) {
198
+ const { virtualCode } = getVirtualCode ( fileName )
199
+ return getComponentSlots ( ts , getProgram ( ) , virtualCode )
200
+ } ,
201
+ getElementAttrs ( fileName , tag ) {
202
+ return getElementAttrs ( ts , getProgram ( ) , fileName , tag )
203
+ } ,
204
+ getElementNames ( fileName ) {
205
+ return getElementNames ( ts , getProgram ( ) , fileName )
206
+ } ,
207
+ getPropertiesAtLocation ( fileName , position ) {
208
+ const { sourceScript, virtualCode } = getVirtualCode ( fileName )
209
+ return getPropertiesAtLocation (
210
+ ts ,
211
+ getLanguageService ( ) . context . language ,
212
+ getProgram ( ) ,
213
+ sourceScript ,
214
+ virtualCode ,
215
+ position ,
216
+ false ,
217
+ )
218
+ } ,
219
+ async getQuickInfoAtPosition ( fileName , position ) {
220
+ const uri = asUri ( fileName )
221
+ const sourceScript =
222
+ getLanguageService ( ) . context . language . scripts . get ( uri )
223
+ if ( ! sourceScript ) {
224
+ return
225
+ }
226
+ const hover = await getLanguageService ( ) . getHover ( uri , position )
227
+ let text = ''
228
+ if ( typeof hover ?. contents === 'string' ) {
229
+ text = hover . contents
230
+ } else if ( Array . isArray ( hover ?. contents ) ) {
231
+ text = hover . contents
232
+ . map ( ( c ) => ( typeof c === 'string' ? c : c . value ) )
233
+ . join ( '\n' )
234
+ } else if ( hover ) {
235
+ text = hover . contents . value
236
+ }
237
+ text = text . replace ( / ` ` ` t y p e s c r i p t / g, '' )
238
+ text = text . replace ( / ` ` ` / g, '' )
239
+ text = text . replace ( / - - - / g, '' )
240
+ text = text . trim ( )
241
+ while ( true ) {
242
+ const newText = text . replace ( / \n \n / g, '\n' )
243
+ if ( newText === text ) {
244
+ break
245
+ }
246
+ text = newText
247
+ }
248
+ text = text . replace ( / \n / g, ' | ' )
249
+ return text
250
+ } ,
251
+ collectExtractProps ( ) {
252
+ throw new Error ( 'Not implemented' )
253
+ } ,
254
+ getImportPathForFile ( ) {
255
+ throw new Error ( 'Not implemented' )
256
+ } ,
257
+ getDocumentHighlights ( ) {
258
+ throw new Error ( 'Not implemented' )
259
+ } ,
260
+ getEncodedSemanticClassifications ( ) {
261
+ throw new Error ( 'Not implemented' )
262
+ } ,
263
+ } )
264
+ const ignoreVueServicePlugins = new Set ( [
265
+ 'vue-extract-file' ,
266
+ 'vue-document-drop' ,
267
+ 'vue-document-highlights' ,
268
+ 'typescript-semantic-tokens' ,
269
+ ] )
270
+ return plugins . filter (
271
+ ( plugin ) => ! ignoreVueServicePlugins . has ( plugin . name ! ) ,
272
+ )
273
+
274
+ function getVirtualCode ( fileName : string ) {
275
+ const uri = asUri ( fileName )
276
+ const sourceScript =
277
+ getLanguageService ( ) . context . language . scripts . get ( uri )
278
+ if ( ! sourceScript ) {
279
+ throw new Error ( 'No source script found for file: ' + fileName )
280
+ }
281
+ const virtualCode = sourceScript . generated ?. root
282
+ if ( ! ( virtualCode instanceof VueVirtualCode ) ) {
283
+ throw new Error ( 'No virtual code found for file: ' + fileName )
284
+ }
285
+ return {
286
+ sourceScript,
287
+ virtualCode,
288
+ }
282
289
}
283
- const virtualCode = sourceScript . generated ?. root
284
- if ( ! ( virtualCode instanceof VueVirtualCode ) ) {
285
- throw new Error ( 'No virtual code found for file: ' + fileName )
290
+
291
+ function getProgram ( ) {
292
+ const tsService : import ( 'typescript' ) . LanguageService =
293
+ getLanguageService ( ) . context . inject ( 'typescript/languageService' )
294
+ return tsService . getProgram ( ) !
286
295
}
287
- return {
288
- sourceScript ,
289
- virtualCode ,
296
+
297
+ function getLanguageService ( ) {
298
+ return ( workerService as any ) . languageService as LanguageService
290
299
}
291
300
}
292
301
} ,
0 commit comments