11import { dirname , join } from 'path' ;
22import ts from 'typescript' ;
3- import { TextDocumentContentChangeEvent } from 'vscode-languageserver' ;
3+ import { RelativePattern , TextDocumentContentChangeEvent } from 'vscode-languageserver' ;
44import { Document , DocumentManager } from '../../lib/documents' ;
55import { LSConfigManager } from '../../ls-config' ;
66import {
@@ -22,7 +22,7 @@ import {
2222import { createProjectService } from './serviceCache' ;
2323import { GlobalSnapshotsManager , SnapshotManager } from './SnapshotManager' ;
2424import { isSubPath } from './utils' ;
25- import { FileMap } from '../../lib/documents/fileCollection' ;
25+ import { FileMap , FileSet } from '../../lib/documents/fileCollection' ;
2626
2727interface LSAndTSDocResolverOptions {
2828 notifyExceedSizeLimit ?: ( ) => void ;
@@ -39,6 +39,8 @@ interface LSAndTSDocResolverOptions {
3939 onProjectReloaded ?: ( ) => void ;
4040 watch ?: boolean ;
4141 tsSystem ?: ts . System ;
42+ watchDirectory ?: ( patterns : RelativePattern [ ] ) => void ;
43+ nonRecursiveWatchPattern ?: string ;
4244}
4345
4446export class LSAndTSDocResolver {
@@ -94,7 +96,17 @@ export class LSAndTSDocResolver {
9496 }
9597 } ) ;
9698
97- this . watchers = new FileMap ( this . tsSystem . useCaseSensitiveFileNames ) ;
99+ this . packageJsonWatchers = new FileMap ( this . tsSystem . useCaseSensitiveFileNames ) ;
100+ this . watchedDirectories = new FileSet ( this . tsSystem . useCaseSensitiveFileNames ) ;
101+
102+ // workspaceUris are already watched during initialization
103+ for ( const root of this . workspaceUris ) {
104+ const rootPath = urlToPath ( root ) ;
105+ if ( rootPath ) {
106+ this . watchedDirectories . add ( rootPath ) ;
107+ }
108+ }
109+
98110 this . lsDocumentContext = {
99111 ambientTypesSource : this . options ?. isSvelteCheck ? 'svelte-check' : 'svelte2tsx' ,
100112 createDocument : this . createDocument ,
@@ -105,7 +117,11 @@ export class LSAndTSDocResolver {
105117 onProjectReloaded : this . options ?. onProjectReloaded ,
106118 watchTsConfig : ! ! this . options ?. watch ,
107119 tsSystem : this . tsSystem ,
108- projectService : projectService
120+ projectService,
121+ watchDirectory : this . options ?. watchDirectory
122+ ? this . watchDirectory . bind ( this )
123+ : undefined ,
124+ nonRecursiveWatchPattern : this . options ?. nonRecursiveWatchPattern
109125 } ;
110126 }
111127
@@ -131,9 +147,9 @@ export class LSAndTSDocResolver {
131147 private getCanonicalFileName : GetCanonicalFileName ;
132148
133149 private userPreferencesAccessor : { preferences : ts . UserPreferences } ;
134- private readonly watchers : FileMap < ts . FileWatcher > ;
135-
150+ private readonly packageJsonWatchers : FileMap < ts . FileWatcher > ;
136151 private lsDocumentContext : LanguageServiceDocumentContext ;
152+ private readonly watchedDirectories : FileSet ;
137153
138154 async getLSForPath ( path : string ) {
139155 return ( await this . getTSService ( path ) ) . getService ( ) ;
@@ -209,15 +225,15 @@ export class LSAndTSDocResolver {
209225 this . docManager . releaseDocument ( uri ) ;
210226 }
211227
212- async invalidateModuleCache ( filePath : string ) {
213- await forAllServices ( ( service ) => service . invalidateModuleCache ( filePath ) ) ;
228+ async invalidateModuleCache ( filePaths : string [ ] ) {
229+ await forAllServices ( ( service ) => service . invalidateModuleCache ( filePaths ) ) ;
214230 }
215231
216232 /**
217233 * Updates project files in all existing ts services
218234 */
219- async updateProjectFiles ( ) {
220- await forAllServices ( ( service ) => service . updateProjectFiles ( ) ) ;
235+ async updateProjectFiles ( watcherNewFiles : string [ ] ) {
236+ await forAllServices ( ( service ) => service . scheduleProjectFileUpdate ( watcherNewFiles ) ) ;
221237 }
222238
223239 /**
@@ -227,6 +243,20 @@ export class LSAndTSDocResolver {
227243 path : string ,
228244 changes ?: TextDocumentContentChangeEvent [ ]
229245 ) : Promise < void > {
246+ await this . updateExistingFile ( path , ( service ) => service . updateTsOrJsFile ( path , changes ) ) ;
247+ }
248+
249+ async updateExistingSvelteFile ( path : string ) : Promise < void > {
250+ const newDocument = this . createDocument ( path , this . tsSystem . readFile ( path ) ?? '' ) ;
251+ await this . updateExistingFile ( path , ( service ) => {
252+ service . updateSnapshot ( newDocument ) ;
253+ } ) ;
254+ }
255+
256+ private async updateExistingFile (
257+ path : string ,
258+ cb : ( service : LanguageServiceContainer ) => void
259+ ) {
230260 path = normalizePath ( path ) ;
231261 // Only update once because all snapshots are shared between
232262 // services. Since we don't have a current version of TS/JS
@@ -235,7 +265,7 @@ export class LSAndTSDocResolver {
235265 await forAllServices ( ( service ) => {
236266 if ( service . hasFile ( path ) && ! didUpdate ) {
237267 didUpdate = true ;
238- service . updateTsOrJsFile ( path , changes ) ;
268+ cb ( service ) ;
239269 }
240270 } ) ;
241271 }
@@ -290,8 +320,8 @@ export class LSAndTSDocResolver {
290320 return {
291321 ...sys ,
292322 readFile : ( path , encoding ) => {
293- if ( path . endsWith ( 'package.json' ) && ! this . watchers . has ( path ) ) {
294- this . watchers . set (
323+ if ( path . endsWith ( 'package.json' ) && ! this . packageJsonWatchers . has ( path ) ) {
324+ this . packageJsonWatchers . set (
295325 path ,
296326 watchFile ( path , this . onPackageJsonWatchChange . bind ( this ) , 3_000 )
297327 ) ;
@@ -309,8 +339,8 @@ export class LSAndTSDocResolver {
309339 const normalizedPath = projectService ?. toPath ( path ) ;
310340
311341 if ( onWatchChange === ts . FileWatcherEventKind . Deleted ) {
312- this . watchers . get ( path ) ?. close ( ) ;
313- this . watchers . delete ( path ) ;
342+ this . packageJsonWatchers . get ( path ) ?. close ( ) ;
343+ this . packageJsonWatchers . delete ( path ) ;
314344 packageJsonCache ?. delete ( normalizedPath ) ;
315345 } else {
316346 packageJsonCache ?. addOrUpdate ( normalizedPath ) ;
@@ -345,4 +375,20 @@ export class LSAndTSDocResolver {
345375 this . globalSnapshotsManager . updateTsOrJsFile ( snapshot . filePath ) ;
346376 } ) ;
347377 }
378+
379+ private watchDirectory ( patterns : RelativePattern [ ] ) {
380+ if ( ! this . options ?. watchDirectory || patterns . length === 0 ) {
381+ return ;
382+ }
383+
384+ for ( const pattern of patterns ) {
385+ const uri = typeof pattern . baseUri === 'string' ? pattern . baseUri : pattern . baseUri . uri ;
386+ for ( const watched of this . watchedDirectories ) {
387+ if ( isSubPath ( watched , uri , this . getCanonicalFileName ) ) {
388+ return ;
389+ }
390+ }
391+ }
392+ this . options . watchDirectory ( patterns ) ;
393+ }
348394}
0 commit comments