@@ -189,13 +189,13 @@ export function postprocessLanguageService<T>(
189189 const getProxyMethod = ( target : ts . LanguageService , p : string | symbol ) : Function | undefined => {
190190 switch ( p ) {
191191 case 'getCompletionsAtPosition' :
192- return getCompletionsAtPosition ( ts , language , asScriptId , vueOptions , target [ p ] ) ;
192+ return getCompletionsAtPosition ( target [ p ] ) ;
193193 case 'getCompletionEntryDetails' :
194- return getCompletionEntryDetails ( language , target [ p ] ) ;
194+ return getCompletionEntryDetails ( target [ p ] ) ;
195195 case 'getCodeFixesAtPosition' :
196196 return getCodeFixesAtPosition ( target [ p ] ) ;
197197 case 'getDefinitionAndBoundSpan' :
198- return getDefinitionAndBoundSpan ( ts , language , asScriptId , languageService , vueOptions , target [ p ] ) ;
198+ return getDefinitionAndBoundSpan ( target [ p ] ) ;
199199 }
200200 } ;
201201
@@ -214,44 +214,178 @@ export function postprocessLanguageService<T>(
214214 return Reflect . set ( target , p , value , receiver ) ;
215215 } ,
216216 } ) ;
217- }
218217
219- function getCompletionsAtPosition < T > (
220- ts : typeof import ( 'typescript' ) ,
221- language : Language < T > ,
222- asScriptId : ( fileName : string ) => T ,
223- vueOptions : VueCompilerOptions ,
224- getCompletionsAtPosition : ts . LanguageService [ 'getCompletionsAtPosition' ] ,
225- ) : ts . LanguageService [ 'getCompletionsAtPosition' ] {
226- return ( filePath , position , options , formattingSettings ) => {
227- const fileName = filePath . replace ( windowsPathReg , '/' ) ;
228- const result = getCompletionsAtPosition ( fileName , position , options , formattingSettings ) ;
229- if ( result ) {
230- resolveCompletionResult (
231- ts ,
232- language ,
233- asScriptId ,
234- vueOptions ,
235- fileName ,
236- position ,
237- result ,
238- ) ;
239- }
240- return result ;
241- } ;
242- }
218+ function getCompletionsAtPosition (
219+ getCompletionsAtPosition : ts . LanguageService [ 'getCompletionsAtPosition' ] ,
220+ ) : ts . LanguageService [ 'getCompletionsAtPosition' ] {
221+ return ( filePath , position , options , formattingSettings ) => {
222+ const fileName = filePath . replace ( windowsPathReg , '/' ) ;
223+ const result = getCompletionsAtPosition ( fileName , position , options , formattingSettings ) ;
224+ if ( result ) {
225+ resolveCompletionResult (
226+ ts ,
227+ language ,
228+ asScriptId ,
229+ vueOptions ,
230+ fileName ,
231+ position ,
232+ result ,
233+ ) ;
234+ }
235+ return result ;
236+ } ;
237+ }
243238
244- function getCompletionEntryDetails < T > (
245- language : Language < T > ,
246- getCompletionEntryDetails : ts . LanguageService [ 'getCompletionEntryDetails' ] ,
247- ) : ts . LanguageService [ 'getCompletionEntryDetails' ] {
248- return ( ...args ) => {
249- const details = getCompletionEntryDetails ( ...args ) ;
250- if ( details ) {
251- resolveCompletionEntryDetails ( language , details , args [ 6 ] ) ;
252- }
253- return details ;
254- } ;
239+ function getCompletionEntryDetails (
240+ getCompletionEntryDetails : ts . LanguageService [ 'getCompletionEntryDetails' ] ,
241+ ) : ts . LanguageService [ 'getCompletionEntryDetails' ] {
242+ return ( ...args ) => {
243+ const details = getCompletionEntryDetails ( ...args ) ;
244+ if ( details ) {
245+ resolveCompletionEntryDetails ( language , details , args [ 6 ] ) ;
246+ }
247+ return details ;
248+ } ;
249+ }
250+
251+ function getCodeFixesAtPosition (
252+ getCodeFixesAtPosition : ts . LanguageService [ 'getCodeFixesAtPosition' ] ,
253+ ) : ts . LanguageService [ 'getCodeFixesAtPosition' ] {
254+ return ( ...args ) => {
255+ let result = getCodeFixesAtPosition ( ...args ) ;
256+ // filter __VLS_
257+ result = result . filter ( entry => ! entry . description . includes ( '__VLS_' ) ) ;
258+ return result ;
259+ } ;
260+ }
261+
262+ function getDefinitionAndBoundSpan (
263+ getDefinitionAndBoundSpan : ts . LanguageService [ 'getDefinitionAndBoundSpan' ] ,
264+ ) : ts . LanguageService [ 'getDefinitionAndBoundSpan' ] {
265+ return ( fileName , position ) => {
266+ const result = getDefinitionAndBoundSpan ( fileName , position ) ;
267+
268+ const program = languageService . getProgram ( ) ! ;
269+ const sourceScript = language . scripts . get ( asScriptId ( fileName ) ) ;
270+ const root = sourceScript ?. generated ?. root ;
271+ if ( ! ( root instanceof VueVirtualCode ) ) {
272+ return result ;
273+ }
274+
275+ if ( ! result ?. definitions ?. length ) {
276+ const { template } = root . sfc ;
277+ if ( template ) {
278+ const textSpan = {
279+ start : template . start + 1 ,
280+ length : 'template' . length ,
281+ } ;
282+ if ( position >= textSpan . start && position <= textSpan . start + textSpan . length ) {
283+ return {
284+ textSpan,
285+ definitions : [ {
286+ fileName,
287+ textSpan,
288+ kind : ts . ScriptElementKind . scriptElement ,
289+ name : fileName ,
290+ containerKind : ts . ScriptElementKind . unknown ,
291+ containerName : '' ,
292+ } ] ,
293+ } ;
294+ }
295+ }
296+ return ;
297+ }
298+
299+ if (
300+ ! root . sfc . template
301+ || position < root . sfc . template . startTagEnd
302+ || position > root . sfc . template . endTagStart
303+ ) {
304+ return result ;
305+ }
306+
307+ const definitions = new Set < ts . DefinitionInfo > ( result . definitions ) ;
308+ const skippedDefinitions : ts . DefinitionInfo [ ] = [ ] ;
309+
310+ // #5275
311+ if ( result . definitions . length >= 2 ) {
312+ for ( const definition of result . definitions ) {
313+ if (
314+ root . sfc . content [ definition . textSpan . start - 1 ] === '@'
315+ || root . sfc . content . slice ( definition . textSpan . start - 5 , definition . textSpan . start ) === 'v-on:'
316+ ) {
317+ definitions . delete ( definition ) ;
318+ }
319+ }
320+ }
321+
322+ for ( const definition of result . definitions ) {
323+ if ( vueOptions . extensions . some ( ext => definition . fileName . endsWith ( ext ) ) ) {
324+ continue ;
325+ }
326+
327+ const sourceFile = program . getSourceFile ( definition . fileName ) ;
328+ if ( ! sourceFile ) {
329+ continue ;
330+ }
331+
332+ visit ( sourceFile , definition , sourceFile ) ;
333+ }
334+
335+ for ( const definition of skippedDefinitions ) {
336+ definitions . delete ( definition ) ;
337+ }
338+
339+ return {
340+ definitions : [ ...definitions ] ,
341+ textSpan : result . textSpan ,
342+ } ;
343+
344+ function visit (
345+ node : ts . Node ,
346+ definition : ts . DefinitionInfo ,
347+ sourceFile : ts . SourceFile ,
348+ ) {
349+ if ( ts . isPropertySignature ( node ) && node . type ) {
350+ proxy ( node . name , node . type , definition , sourceFile ) ;
351+ }
352+ else if ( ts . isVariableDeclaration ( node ) && ts . isIdentifier ( node . name ) && node . type && ! node . initializer ) {
353+ proxy ( node . name , node . type , definition , sourceFile ) ;
354+ }
355+ else {
356+ ts . forEachChild ( node , child => visit ( child , definition , sourceFile ) ) ;
357+ }
358+ }
359+
360+ function proxy (
361+ name : ts . PropertyName ,
362+ type : ts . TypeNode ,
363+ definition : ts . DefinitionInfo ,
364+ sourceFile : ts . SourceFile ,
365+ ) {
366+ const { textSpan, fileName } = definition ;
367+ const start = name . getStart ( sourceFile ) ;
368+ const end = name . getEnd ( ) ;
369+
370+ if ( start !== textSpan . start || end - start !== textSpan . length ) {
371+ return ;
372+ }
373+
374+ if ( ! ts . isIndexedAccessTypeNode ( type ) ) {
375+ return ;
376+ }
377+
378+ const pos = type . indexType . getStart ( sourceFile ) ;
379+ const res = getDefinitionAndBoundSpan ( fileName , pos ) ;
380+ if ( res ?. definitions ?. length ) {
381+ for ( const definition of res . definitions ) {
382+ definitions . add ( definition ) ;
383+ }
384+ skippedDefinitions . push ( definition ) ;
385+ }
386+ }
387+ } ;
388+ }
255389}
256390
257391export function resolveCompletionResult < T > (
@@ -375,147 +509,3 @@ export function resolveCompletionEntryDetails(
375509 }
376510 }
377511}
378-
379- function getCodeFixesAtPosition (
380- getCodeFixesAtPosition : ts . LanguageService [ 'getCodeFixesAtPosition' ] ,
381- ) : ts . LanguageService [ 'getCodeFixesAtPosition' ] {
382- return ( ...args ) => {
383- let result = getCodeFixesAtPosition ( ...args ) ;
384- // filter __VLS_
385- result = result . filter ( entry => ! entry . description . includes ( '__VLS_' ) ) ;
386- return result ;
387- } ;
388- }
389-
390- function getDefinitionAndBoundSpan < T > (
391- ts : typeof import ( 'typescript' ) ,
392- language : Language < T > ,
393- asScriptId : ( fileName : string ) => T ,
394- languageService : ts . LanguageService ,
395- vueOptions : VueCompilerOptions ,
396- getDefinitionAndBoundSpan : ts . LanguageService [ 'getDefinitionAndBoundSpan' ] ,
397- ) : ts . LanguageService [ 'getDefinitionAndBoundSpan' ] {
398- return ( fileName , position ) => {
399- const result = getDefinitionAndBoundSpan ( fileName , position ) ;
400-
401- const program = languageService . getProgram ( ) ! ;
402- const sourceScript = language . scripts . get ( asScriptId ( fileName ) ) ;
403- const root = sourceScript ?. generated ?. root ;
404- if ( ! ( root instanceof VueVirtualCode ) ) {
405- return result ;
406- }
407-
408- if ( ! result ?. definitions ?. length ) {
409- const { template } = root . sfc ;
410- if ( template ) {
411- const textSpan = {
412- start : template . start + 1 ,
413- length : 'template' . length ,
414- } ;
415- if ( position >= textSpan . start && position <= textSpan . start + textSpan . length ) {
416- return {
417- textSpan,
418- definitions : [ {
419- fileName,
420- textSpan,
421- kind : ts . ScriptElementKind . scriptElement ,
422- name : fileName ,
423- containerKind : ts . ScriptElementKind . unknown ,
424- containerName : '' ,
425- } ] ,
426- } ;
427- }
428- }
429- return ;
430- }
431-
432- if (
433- ! root . sfc . template
434- || position < root . sfc . template . startTagEnd
435- || position > root . sfc . template . endTagStart
436- ) {
437- return result ;
438- }
439-
440- const definitions = new Set < ts . DefinitionInfo > ( result . definitions ) ;
441- const skippedDefinitions : ts . DefinitionInfo [ ] = [ ] ;
442-
443- // #5275
444- if ( result . definitions . length >= 2 ) {
445- for ( const definition of result . definitions ) {
446- if (
447- root . sfc . content [ definition . textSpan . start - 1 ] === '@'
448- || root . sfc . content . slice ( definition . textSpan . start - 5 , definition . textSpan . start ) === 'v-on:'
449- ) {
450- definitions . delete ( definition ) ;
451- }
452- }
453- }
454-
455- for ( const definition of result . definitions ) {
456- if ( vueOptions . extensions . some ( ext => definition . fileName . endsWith ( ext ) ) ) {
457- continue ;
458- }
459-
460- const sourceFile = program . getSourceFile ( definition . fileName ) ;
461- if ( ! sourceFile ) {
462- continue ;
463- }
464-
465- visit ( sourceFile , definition , sourceFile ) ;
466- }
467-
468- for ( const definition of skippedDefinitions ) {
469- definitions . delete ( definition ) ;
470- }
471-
472- return {
473- definitions : [ ...definitions ] ,
474- textSpan : result . textSpan ,
475- } ;
476-
477- function visit (
478- node : ts . Node ,
479- definition : ts . DefinitionInfo ,
480- sourceFile : ts . SourceFile ,
481- ) {
482- if ( ts . isPropertySignature ( node ) && node . type ) {
483- proxy ( node . name , node . type , definition , sourceFile ) ;
484- }
485- else if ( ts . isVariableDeclaration ( node ) && ts . isIdentifier ( node . name ) && node . type && ! node . initializer ) {
486- proxy ( node . name , node . type , definition , sourceFile ) ;
487- }
488- else {
489- ts . forEachChild ( node , child => visit ( child , definition , sourceFile ) ) ;
490- }
491- }
492-
493- function proxy (
494- name : ts . PropertyName ,
495- type : ts . TypeNode ,
496- definition : ts . DefinitionInfo ,
497- sourceFile : ts . SourceFile ,
498- ) {
499- const { textSpan, fileName } = definition ;
500- const start = name . getStart ( sourceFile ) ;
501- const end = name . getEnd ( ) ;
502-
503- if ( start !== textSpan . start || end - start !== textSpan . length ) {
504- return ;
505- }
506-
507- if ( ! ts . isIndexedAccessTypeNode ( type ) ) {
508- return ;
509- }
510-
511- const pos = type . indexType . getStart ( sourceFile ) ;
512- const res = getDefinitionAndBoundSpan ( fileName , pos ) ;
513- if ( res ?. definitions ?. length ) {
514- for ( const definition of res . definitions ) {
515- definitions . add ( definition ) ;
516- }
517- skippedDefinitions . push ( definition ) ;
518- }
519- }
520- } ;
521- }
0 commit comments