1
1
import type { LanguageServiceContext , LanguageServicePlugin } from '@volar/language-service' ;
2
- import { hyphenateAttr } from '@vue/language-core' ;
2
+ import { hyphenateAttr , VueVirtualCode } from '@vue/language-core' ;
3
3
import type * as ts from 'typescript' ;
4
4
import type { TextDocument } from 'vscode-languageserver-textdocument' ;
5
5
import { URI } from 'vscode-uri' ;
6
-
7
- const asts = new WeakMap < ts . IScriptSnapshot , ts . SourceFile > ( ) ;
8
-
9
- function getAst ( ts : typeof import ( 'typescript' ) , fileName : string , snapshot : ts . IScriptSnapshot , scriptKind ?: ts . ScriptKind ) {
10
- let ast = asts . get ( snapshot ) ;
11
- if ( ! ast ) {
12
- ast = ts . createSourceFile ( fileName , snapshot . getText ( 0 , snapshot . getLength ( ) ) , ts . ScriptTarget . Latest , undefined , scriptKind ) ;
13
- asts . set ( snapshot , ast ) ;
14
- }
15
- return ast ;
16
- }
6
+ import { isTsDocument , sleep } from './utils' ;
17
7
18
8
export function create (
19
9
ts : typeof import ( 'typescript' ) ,
@@ -61,46 +51,49 @@ export function create(
61
51
const decoded = context . decodeEmbeddedDocumentUri ( uri ) ;
62
52
const sourceScript = decoded && context . language . scripts . get ( decoded [ 0 ] ) ;
63
53
const virtualCode = decoded && sourceScript ?. generated ?. embeddedCodes . get ( decoded [ 1 ] ) ;
64
- if ( ! sourceScript ) {
54
+ if ( ! sourceScript ?. generated || ! virtualCode ) {
65
55
return ;
66
56
}
67
57
68
- let ast : ts . SourceFile | undefined ;
69
- let sourceCodeOffset = document . offsetAt ( selection ) ;
58
+ const root = sourceScript . generated . root ;
59
+ if ( ! ( root instanceof VueVirtualCode ) ) {
60
+ return ;
61
+ }
70
62
71
- const fileName = context . project . typescript ?. uriConverter . asFileName ( sourceScript . id )
72
- ?? sourceScript . id . fsPath . replace ( / \\ / g, '/' ) ;
63
+ const blocks = [
64
+ root . _sfc . script ,
65
+ root . _sfc . scriptSetup ,
66
+ ] . filter ( block => ! ! block ) ;
67
+ if ( ! blocks . length ) {
68
+ return ;
69
+ }
73
70
74
- if ( sourceScript . generated ) {
75
- const serviceScript = sourceScript . generated . languagePlugin . typescript ?. getServiceScript ( sourceScript . generated . root ) ;
76
- if ( ! serviceScript || serviceScript ?. code !== virtualCode ) {
77
- return ;
78
- }
79
- ast = getAst ( ts , fileName , virtualCode . snapshot , serviceScript . scriptKind ) ;
80
- let mapped = false ;
81
- for ( const [ _sourceScript , map ] of context . language . maps . forEach ( virtualCode ) ) {
82
- for ( const [ sourceOffset ] of map . toSourceLocation ( document . offsetAt ( selection ) ) ) {
83
- sourceCodeOffset = sourceOffset ;
84
- mapped = true ;
85
- break ;
86
- }
87
- if ( mapped ) {
88
- break ;
89
- }
71
+ let sourceCodeOffset = document . offsetAt ( selection ) ;
72
+ let mapped = false ;
73
+ for ( const [ , map ] of context . language . maps . forEach ( virtualCode ) ) {
74
+ for ( const [ sourceOffset ] of map . toSourceLocation ( sourceCodeOffset ) ) {
75
+ sourceCodeOffset = sourceOffset ;
76
+ mapped = true ;
77
+ break ;
90
78
}
91
- if ( ! mapped ) {
92
- return ;
79
+ if ( mapped ) {
80
+ break ;
93
81
}
94
82
}
95
- else {
96
- ast = getAst ( ts , fileName , sourceScript . snapshot ) ;
83
+ if ( ! mapped ) {
84
+ return ;
97
85
}
98
86
99
- if ( isBlacklistNode ( ts , ast , document . offsetAt ( selection ) , false ) ) {
100
- return ;
87
+ for ( const { ast, startTagEnd, endTagStart } of blocks ) {
88
+ if ( sourceCodeOffset < startTagEnd || sourceCodeOffset > endTagStart ) {
89
+ continue ;
90
+ }
91
+ if ( isBlacklistNode ( ts , ast , sourceCodeOffset - startTagEnd , false ) ) {
92
+ return ;
93
+ }
101
94
}
102
95
103
- const props = await tsPluginClient ?. getPropertiesAtLocation ( fileName , sourceCodeOffset ) ?? [ ] ;
96
+ const props = await tsPluginClient ?. getPropertiesAtLocation ( root . fileName , sourceCodeOffset ) ?? [ ] ;
104
97
if ( props . some ( prop => prop === 'value' ) ) {
105
98
return '${1:.value}' ;
106
99
}
@@ -110,20 +103,9 @@ export function create(
110
103
} ;
111
104
}
112
105
113
- function sleep ( ms : number ) {
114
- return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
115
- }
116
-
117
- export function isTsDocument ( document : TextDocument ) {
118
- return document . languageId === 'javascript' ||
119
- document . languageId === 'typescript' ||
120
- document . languageId === 'javascriptreact' ||
121
- document . languageId === 'typescriptreact' ;
122
- }
123
-
124
106
const charReg = / \w / ;
125
107
126
- export function isCharacterTyping ( document : TextDocument , change : { text : string ; rangeOffset : number ; rangeLength : number ; } ) {
108
+ function isCharacterTyping ( document : TextDocument , change : { text : string ; rangeOffset : number ; rangeLength : number ; } ) {
127
109
const lastCharacter = change . text [ change . text . length - 1 ] ;
128
110
const nextCharacter = document . getText ( ) . slice (
129
111
change . rangeOffset + change . text . length ,
@@ -138,7 +120,7 @@ export function isCharacterTyping(document: TextDocument, change: { text: string
138
120
return charReg . test ( lastCharacter ) && ! charReg . test ( nextCharacter ) ;
139
121
}
140
122
141
- export function isBlacklistNode ( ts : typeof import ( 'typescript' ) , node : ts . Node , pos : number , allowAccessDotValue : boolean ) {
123
+ function isBlacklistNode ( ts : typeof import ( 'typescript' ) , node : ts . Node , pos : number , allowAccessDotValue : boolean ) {
142
124
if ( ts . isVariableDeclaration ( node ) && pos >= node . name . getFullStart ( ) && pos <= node . name . getEnd ( ) ) {
143
125
return true ;
144
126
}
0 commit comments