@@ -19,7 +19,8 @@ import {
1919import type * as monaco from 'monaco-editor-core'
2020// @ts -expect-error
2121import * 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'
2324import { URI } from 'vscode-uri'
2425import type { WorkerHost , WorkerMessage } from './env'
2526
@@ -91,165 +92,7 @@ self.onmessage = async (msg: MessageEvent<WorkerMessage>) => {
9192 ...getDefaultCompilerOptions ( ) ,
9293 ...tsconfig . vueCompilerOptions ,
9394 }
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 )
25396
25497 const workerService = createTypeScriptWorkerLanguageService ( {
25598 typescript : ts ,
@@ -260,33 +103,199 @@ self.onmessage = async (msg: MessageEvent<WorkerMessage>) => {
260103 asFileName,
261104 asUri,
262105 } ,
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+ ] ,
265118 } )
266- const languageService = ( workerService as any )
267- . languageService as LanguageService
268119
269120 return workerService
270121
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 ( ) ]
275181 }
276182
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+ }
282289 }
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 ( ) !
286295 }
287- return {
288- sourceScript ,
289- virtualCode ,
296+
297+ function getLanguageService ( ) {
298+ return ( workerService as any ) . languageService as LanguageService
290299 }
291300 }
292301 } ,
0 commit comments