@@ -26,14 +26,14 @@ import {
26
26
CompletionParams ,
27
27
ExecuteCommandParams ,
28
28
ApplyWorkspaceEditRequest ,
29
- FoldingRangeParams
29
+ FoldingRangeParams ,
30
+ DidChangeWorkspaceFoldersNotification
30
31
} from 'vscode-languageserver' ;
31
32
import {
32
33
ColorInformation ,
33
34
CompletionItem ,
34
35
CompletionList ,
35
36
Definition ,
36
- Diagnostic ,
37
37
DocumentHighlight ,
38
38
DocumentLink ,
39
39
Hover ,
@@ -48,85 +48,68 @@ import {
48
48
import type { TextDocument } from 'vscode-languageserver-textdocument' ;
49
49
50
50
import { URI } from 'vscode-uri' ;
51
- import { LanguageModes , LanguageModeRange , LanguageMode } from '../embeddedSupport/languageModes' ;
52
51
import { NULL_COMPLETION , NULL_HOVER , NULL_SIGNATURE } from '../modes/nullMode' ;
53
- import { VueInfoService } from './vueInfoService' ;
54
- import { createDependencyService , DependencyService } from './dependencyService' ;
52
+ import { createDependencyService , createNodeModulesPaths } from './dependencyService' ;
55
53
import _ from 'lodash' ;
56
- import { DocumentContext , RefactorAction } from '../types' ;
54
+ import { RefactorAction } from '../types' ;
57
55
import { DocumentService } from './documentService' ;
58
56
import { VueHTMLMode } from '../modes/template' ;
59
57
import { logger } from '../log' ;
60
- import { getDefaultVLSConfig , VLSFullConfig , VLSConfig , getVeturFullConfig , VeturFullConfig } from '../config' ;
61
- import { LanguageId } from '../embeddedSupport/embeddedSupport' ;
58
+ import { getDefaultVLSConfig , VLSFullConfig , getVeturFullConfig , VeturFullConfig } from '../config' ;
62
59
import { APPLY_REFACTOR_COMMAND } from '../modes/script/javascript' ;
63
60
import { VCancellationToken , VCancellationTokenSource } from '../utils/cancellationToken' ;
64
61
import { findConfigFile } from '../utils/workspace' ;
65
62
import { createProjectService , ProjectService } from './projectService' ;
66
63
67
64
export class VLS {
68
65
// @Todo : Remove this and DocumentContext
69
- private workspacePath : string | undefined ;
70
- private veturConfig : VeturFullConfig ;
66
+ // private workspacePath: string | undefined;
67
+ private workspaces : Map < string , VeturFullConfig & { workspaceFsPath : string } > ;
68
+ private nodeModulesMap : Map < string , string [ ] > ;
69
+ // private veturConfig: VeturFullConfig;
71
70
private documentService : DocumentService ;
72
- private rootPathForConfig : string ;
71
+ // private rootPathForConfig: string;
73
72
private globalSnippetDir : string ;
74
73
private projects : Map < string , ProjectService > ;
75
- private dependencyService : DependencyService ;
74
+ // private dependencyService: DependencyService;
76
75
77
76
private pendingValidationRequests : { [ uri : string ] : NodeJS . Timer } = { } ;
78
77
private cancellationTokenValidationRequests : { [ uri : string ] : VCancellationTokenSource } = { } ;
79
78
private validationDelayMs = 200 ;
80
- private validation : { [ k : string ] : boolean } = {
81
- 'vue-html' : true ,
82
- html : true ,
83
- css : true ,
84
- scss : true ,
85
- less : true ,
86
- postcss : true ,
87
- javascript : true
88
- } ;
89
- private templateInterpolationValidation = false ;
90
79
91
80
private documentFormatterRegistration : Disposable | undefined ;
92
81
93
- private config : VLSFullConfig ;
82
+ private workspaceConfig : VLSFullConfig ;
94
83
95
84
constructor ( private lspConnection : Connection ) {
96
85
this . documentService = new DocumentService ( this . lspConnection ) ;
97
- this . dependencyService = createDependencyService ( ) ;
86
+ this . projects = new Map ( ) ;
87
+ this . nodeModulesMap = new Map ( ) ;
98
88
}
99
89
100
90
async init ( params : InitializeParams ) {
101
- const workspacePath = params . rootPath ;
102
- if ( ! workspacePath ) {
91
+ let workspaceFolders =
92
+ ! Array . isArray ( params . workspaceFolders ) && params . rootPath
93
+ ? [ { name : '' , fsPath : normalizeFileNameToFsPath ( params . rootPath ) } ]
94
+ : params . workspaceFolders ?. map ( el => ( { name : el . name , fsPath : getFileFsPath ( el . uri ) } ) ) ?? [ ] ;
95
+
96
+ if ( workspaceFolders . length === 0 ) {
103
97
console . error ( 'No workspace path found. Vetur initialization failed.' ) ;
104
98
return {
105
99
capabilities : { }
106
100
} ;
107
101
}
108
102
109
- this . workspacePath = normalizeFileNameToFsPath ( workspacePath ) ;
110
-
103
+ this . workspaces = new Map ( ) ;
111
104
this . globalSnippetDir = params . initializationOptions ?. globalSnippetDir ;
112
- const veturConfigPath = findConfigFile ( this . workspacePath , 'vetur.config.js' ) ;
113
- this . rootPathForConfig = normalizeFileNameToFsPath ( veturConfigPath ? path . dirname ( veturConfigPath ) : workspacePath ) ;
114
- this . veturConfig = await getVeturFullConfig (
115
- this . rootPathForConfig ,
116
- this . workspacePath ,
117
- veturConfigPath ? require ( veturConfigPath ) : { }
118
- ) ;
119
- const config = this . getFullConfig ( params . initializationOptions ?. config ) ;
120
105
121
- await this . dependencyService . init (
122
- this . rootPathForConfig ,
123
- this . workspacePath ,
124
- config . vetur . useWorkspaceDependencies ,
125
- config . typescript . tsdk
126
- ) ;
127
- this . projects = new Map ( ) ;
106
+ await Promise . all ( workspaceFolders . map ( workspace => this . addWorkspace ( workspace ) ) ) ;
107
+
108
+ this . workspaceConfig = this . getVLSFullConfig ( { } , params . initializationOptions ?. config ) ;
128
109
129
- this . configure ( config ) ;
110
+ if ( params . capabilities . workspace ?. workspaceFolders ) {
111
+ this . setupWorkspaceListeners ( ) ;
112
+ }
130
113
this . setupConfigListeners ( ) ;
131
114
this . setupLSPHandlers ( ) ;
132
115
this . setupCustomLSPHandlers ( ) ;
@@ -141,28 +124,70 @@ export class VLS {
141
124
this . lspConnection . listen ( ) ;
142
125
}
143
126
144
- private getFullConfig ( config : any | undefined ) : VLSFullConfig {
127
+ private getVLSFullConfig ( settings : VeturFullConfig [ 'settings' ] , config : any | undefined ) : VLSFullConfig {
145
128
const result = config ? _ . merge ( getDefaultVLSConfig ( ) , config ) : getDefaultVLSConfig ( ) ;
146
- Object . keys ( this . veturConfig . settings ) . forEach ( key => {
147
- _ . set ( result , key , this . veturConfig . settings [ key ] ) ;
129
+ Object . keys ( settings ) . forEach ( key => {
130
+ _ . set ( result , key , settings [ key ] ) ;
148
131
} ) ;
149
132
return result ;
150
133
}
151
134
135
+ private async addWorkspace ( workspace : { name : string ; fsPath : string } ) {
136
+ const veturConfigPath = findConfigFile ( workspace . fsPath , 'vetur.config.js' ) ;
137
+ const rootPathForConfig = normalizeFileNameToFsPath (
138
+ veturConfigPath ? path . dirname ( veturConfigPath ) : workspace . fsPath
139
+ ) ;
140
+ if ( ! this . workspaces . has ( rootPathForConfig ) ) {
141
+ this . workspaces . set ( rootPathForConfig , {
142
+ ...( await getVeturFullConfig (
143
+ rootPathForConfig ,
144
+ workspace . fsPath ,
145
+ veturConfigPath ? require ( veturConfigPath ) : { }
146
+ ) ) ,
147
+ workspaceFsPath : workspace . fsPath
148
+ } ) ;
149
+ }
150
+ }
151
+
152
+ private setupWorkspaceListeners ( ) {
153
+ this . lspConnection . client . register ( DidChangeWorkspaceFoldersNotification . type ) ;
154
+ this . lspConnection . workspace . onDidChangeWorkspaceFolders ( async e => {
155
+ await Promise . all ( e . added . map ( el => this . addWorkspace ( { name : el . name , fsPath : getFileFsPath ( el . uri ) } ) ) ) ;
156
+ } ) ;
157
+ }
158
+
152
159
private setupConfigListeners ( ) {
153
160
this . lspConnection . onDidChangeConfiguration ( async ( { settings } : DidChangeConfigurationParams ) => {
154
- const config = this . getFullConfig ( settings ) ;
155
- this . configure ( config ) ;
156
- this . setupDynamicFormatters ( config ) ;
161
+ let isFormatEnable = false ;
162
+ this . projects . forEach ( project => {
163
+ const veturConfig = this . workspaces . get ( project . rootPathForConfig ) ;
164
+ if ( ! veturConfig ) return ;
165
+ const fullConfig = this . getVLSFullConfig ( veturConfig . settings , settings ) ;
166
+ project . configure ( fullConfig ) ;
167
+ isFormatEnable = isFormatEnable || fullConfig . vetur . format . enable ;
168
+ } ) ;
169
+ this . setupDynamicFormatters ( isFormatEnable ) ;
157
170
} ) ;
158
171
159
172
this . documentService . getAllDocuments ( ) . forEach ( this . triggerValidation ) ;
160
173
}
161
174
162
175
private async getProjectService ( uri : DocumentUri ) : Promise < ProjectService | undefined > {
163
- const projectRootPaths = this . veturConfig . projects
176
+ const projectRootPaths = _ . flatten (
177
+ Array . from ( this . workspaces . entries ( ) ) . map ( ( [ rootPathForConfig , veturConfig ] ) =>
178
+ veturConfig . projects . map ( project => ( {
179
+ ...project ,
180
+ rootPathForConfig,
181
+ vlsFullConfig : this . getVLSFullConfig ( veturConfig . settings , this . workspaceConfig ) ,
182
+ workspaceFsPath : veturConfig . workspaceFsPath
183
+ } ) )
184
+ )
185
+ )
164
186
. map ( project => ( {
165
- rootFsPath : normalizeFileNameResolve ( this . rootPathForConfig , project . root ) ,
187
+ vlsFullConfig : project . vlsFullConfig ,
188
+ rootPathForConfig : project . rootPathForConfig ,
189
+ workspaceFsPath : project . workspaceFsPath ,
190
+ rootFsPath : normalizeFileNameResolve ( project . rootPathForConfig , project . root ) ,
166
191
tsconfigPath : project . tsconfig ,
167
192
packagePath : project . package ,
168
193
snippetFolder : project . snippetFolder ,
@@ -178,18 +203,31 @@ export class VLS {
178
203
return this . projects . get ( projectConfig . rootFsPath ) ;
179
204
}
180
205
206
+ const dependencyService = createDependencyService ( ) ;
207
+ const nodeModulePaths =
208
+ this . nodeModulesMap . get ( projectConfig . rootPathForConfig ) ??
209
+ createNodeModulesPaths ( projectConfig . rootPathForConfig ) ;
210
+ if ( this . nodeModulesMap . has ( projectConfig . rootPathForConfig ) ) {
211
+ this . nodeModulesMap . set ( projectConfig . rootPathForConfig , nodeModulePaths ) ;
212
+ }
213
+ await dependencyService . init (
214
+ projectConfig . rootPathForConfig ,
215
+ projectConfig . workspaceFsPath ,
216
+ projectConfig . vlsFullConfig . vetur . useWorkspaceDependencies ,
217
+ nodeModulePaths ,
218
+ projectConfig . vlsFullConfig . typescript . tsdk
219
+ ) ;
181
220
const project = await createProjectService (
182
- this . rootPathForConfig ,
183
- this . workspacePath ?? this . rootPathForConfig ,
221
+ projectConfig . rootPathForConfig ,
184
222
projectConfig . rootFsPath ,
185
223
projectConfig . tsconfigPath ,
186
224
projectConfig . packagePath ,
187
225
projectConfig . snippetFolder ,
188
226
projectConfig . globalComponents ,
189
227
this . documentService ,
190
- this . config ,
228
+ this . workspaceConfig ,
191
229
this . globalSnippetDir ,
192
- this . dependencyService
230
+ dependencyService
193
231
) ;
194
232
this . projects . set ( projectConfig . rootFsPath , project ) ;
195
233
return project ;
@@ -232,8 +270,8 @@ export class VLS {
232
270
} ) ;
233
271
}
234
272
235
- private async setupDynamicFormatters ( settings : VLSFullConfig ) {
236
- if ( settings . vetur . format . enable ) {
273
+ private async setupDynamicFormatters ( enable : boolean ) {
274
+ if ( enable ) {
237
275
if ( ! this . documentFormatterRegistration ) {
238
276
this . documentFormatterRegistration = await this . lspConnection . client . register ( DocumentFormattingRequest . type , {
239
277
documentSelector : [ { language : 'vue' } ]
@@ -270,26 +308,6 @@ export class VLS {
270
308
} ) ;
271
309
}
272
310
273
- configure ( config : VLSConfig ) : void {
274
- this . config = config ;
275
-
276
- const veturValidationOptions = config . vetur . validation ;
277
- this . validation [ 'vue-html' ] = veturValidationOptions . template ;
278
- this . validation . css = veturValidationOptions . style ;
279
- this . validation . postcss = veturValidationOptions . style ;
280
- this . validation . scss = veturValidationOptions . style ;
281
- this . validation . less = veturValidationOptions . style ;
282
- this . validation . javascript = veturValidationOptions . script ;
283
-
284
- this . templateInterpolationValidation = config . vetur . experimental . templateInterpolationService ;
285
-
286
- this . projects . forEach ( project => {
287
- project . configure ( config ) ;
288
- } ) ;
289
-
290
- logger . setLevel ( config . vetur . dev . logLevel ) ;
291
- }
292
-
293
311
/**
294
312
* Custom Notifications
295
313
*/
@@ -407,8 +425,7 @@ export class VLS {
407
425
this . cancelPastValidation ( textDocument ) ;
408
426
this . pendingValidationRequests [ textDocument . uri ] = setTimeout ( ( ) => {
409
427
delete this . pendingValidationRequests [ textDocument . uri ] ;
410
- const tsDep = this . dependencyService . get ( 'typescript' ) ;
411
- this . cancellationTokenValidationRequests [ textDocument . uri ] = new VCancellationTokenSource ( tsDep . module ) ;
428
+ this . cancellationTokenValidationRequests [ textDocument . uri ] = new VCancellationTokenSource ( ) ;
412
429
this . validateTextDocument ( textDocument , this . cancellationTokenValidationRequests [ textDocument . uri ] . token ) ;
413
430
} , this . validationDelayMs ) ;
414
431
}
@@ -470,6 +487,7 @@ export class VLS {
470
487
get capabilities ( ) : ServerCapabilities {
471
488
return {
472
489
textDocumentSync : TextDocumentSyncKind . Incremental ,
490
+ workspace : { workspaceFolders : { supported : true , changeNotifications : true } } ,
473
491
completionProvider : { resolveProvider : true , triggerCharacters : [ '.' , ':' , '<' , '"' , "'" , '/' , '@' , '*' , ' ' ] } ,
474
492
signatureHelpProvider : { triggerCharacters : [ '(' ] } ,
475
493
documentFormattingProvider : false ,
0 commit comments