@@ -12,19 +12,21 @@ import { PromptsConfig } from '../../../../../../platform/prompts/common/config.
12
12
import { basename , dirname , joinPath } from '../../../../../../base/common/resources.js' ;
13
13
import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js' ;
14
14
import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js' ;
15
- import { getPromptFileExtension , getPromptFileType , PromptsType } from '../../../../../../platform/prompts/common/prompts.js' ;
15
+ import { getPromptFileExtension , getPromptFileLocationsConfigKey , getPromptFileType , PromptsType } from '../../../../../../platform/prompts/common/prompts.js' ;
16
16
import { IWorkbenchEnvironmentService } from '../../../../../services/environment/common/environmentService.js' ;
17
17
import { Schemas } from '../../../../../../base/common/network.js' ;
18
18
import { getExcludes , IFileQuery , ISearchConfiguration , ISearchService , QueryType } from '../../../../../services/search/common/search.js' ;
19
19
import { CancellationToken } from '../../../../../../base/common/cancellation.js' ;
20
20
import { isCancellationError } from '../../../../../../base/common/errors.js' ;
21
21
import { TPromptsStorage } from '../service/types.js' ;
22
22
import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js' ;
23
+ import { Emitter , Event } from '../../../../../../base/common/event.js' ;
24
+ import { Disposable } from '../../../../../../base/common/lifecycle.js' ;
23
25
24
26
/**
25
27
* Utility class to locate prompt files.
26
28
*/
27
- export class PromptFilesLocator {
29
+ export class PromptFilesLocator extends Disposable {
28
30
29
31
constructor (
30
32
@IFileService private readonly fileService : IFileService ,
@@ -33,7 +35,9 @@ export class PromptFilesLocator {
33
35
@IWorkbenchEnvironmentService private readonly environmentService : IWorkbenchEnvironmentService ,
34
36
@ISearchService private readonly searchService : ISearchService ,
35
37
@IUserDataProfileService private readonly userDataService : IUserDataProfileService ,
36
- ) { }
38
+ ) {
39
+ super ( ) ;
40
+ }
37
41
38
42
/**
39
43
* List all prompt files from the filesystem.
@@ -42,9 +46,7 @@ export class PromptFilesLocator {
42
46
*/
43
47
public async listFiles ( type : PromptsType , storage : TPromptsStorage , token : CancellationToken ) : Promise < readonly URI [ ] > {
44
48
if ( storage === 'local' ) {
45
- const configuredLocations = PromptsConfig . promptSourceFolders ( this . configService , type ) ;
46
- const absoluteLocations = this . toAbsoluteLocations ( configuredLocations ) ;
47
- return await this . findFilesInLocations ( absoluteLocations , type , token ) ;
49
+ return await this . listFilesInLocal ( type , token ) ;
48
50
} else {
49
51
return await this . listFilesInUserData ( type , token ) ;
50
52
}
@@ -55,6 +57,29 @@ export class PromptFilesLocator {
55
57
return files . filter ( file => getPromptFileType ( file ) === type ) ;
56
58
}
57
59
60
+ public getFilesUpdatedEvent ( type : PromptsType ) : Event < void > {
61
+ const eventEmitter = this . _register ( new Emitter < void > ( ) ) ;
62
+ const key = getPromptFileLocationsConfigKey ( type ) ;
63
+ let parentFolders = this . getLocalParentFolders ( type ) . map ( folder => folder . parent ) ;
64
+ this . _register ( this . configService . onDidChangeConfiguration ( e => {
65
+ if ( e . affectsConfiguration ( key ) ) {
66
+ parentFolders = this . getLocalParentFolders ( type ) . map ( folder => folder . parent ) ;
67
+ eventEmitter . fire ( ) ;
68
+ }
69
+ } ) ) ;
70
+ this . _register ( this . fileService . onDidFilesChange ( e => {
71
+ if ( e . affects ( this . userDataService . currentProfile . promptsHome ) ) {
72
+ eventEmitter . fire ( ) ;
73
+ return ;
74
+ }
75
+ if ( parentFolders . some ( folder => e . affects ( folder ) ) ) {
76
+ eventEmitter . fire ( ) ;
77
+ return ;
78
+ }
79
+ } ) ) ;
80
+ return eventEmitter . event ;
81
+ }
82
+
58
83
/**
59
84
* Get all possible unambiguous prompt file source folders based on
60
85
* the current workspace folder structure.
@@ -107,29 +132,19 @@ export class PromptFilesLocator {
107
132
}
108
133
109
134
/**
110
- * Finds all existent prompt files in the provided source folders.
111
- *
112
- * @throws if any of the provided folder paths is not an `absolute path`.
135
+ * Finds all existent prompt files in the configured local source folders.
113
136
*
114
- * @param absoluteLocations List of prompt file source folders to search for prompt files in. Must be absolute paths.
115
- * @returns List of prompt files found in the provided source folders.
137
+ * @returns List of prompt files found in the local source folders.
116
138
*/
117
- private async findFilesInLocations (
118
- absoluteLocations : readonly URI [ ] ,
139
+ private async listFilesInLocal (
119
140
type : PromptsType ,
120
141
token : CancellationToken
121
142
) : Promise < readonly URI [ ] > {
122
143
// find all prompt files in the provided locations, then match
123
144
// the found file paths against (possible) glob patterns
124
145
const paths = new ResourceSet ( ) ;
125
- for ( const absoluteLocation of absoluteLocations ) {
126
- assert (
127
- isAbsolute ( absoluteLocation . path ) ,
128
- `Provided location must be an absolute path, got '${ absoluteLocation . path } '.` ,
129
- ) ;
130
-
131
- const { parent, filePattern } = firstNonGlobParentAndPattern ( absoluteLocation ) ;
132
146
147
+ for ( const { parent, filePattern } of this . getLocalParentFolders ( type ) ) {
133
148
const files = ( filePattern === undefined )
134
149
? await this . resolveFilesAtLocation ( parent , token ) // if the location does not contain a glob pattern, resolve the location directly
135
150
: await this . searchFilesInLocation ( parent , filePattern , token ) ;
@@ -146,6 +161,12 @@ export class PromptFilesLocator {
146
161
return [ ...paths ] ;
147
162
}
148
163
164
+ private getLocalParentFolders ( type : PromptsType ) : readonly { parent : URI ; filePattern ?: string } [ ] {
165
+ const configuredLocations = PromptsConfig . promptSourceFolders ( this . configService , type ) ;
166
+ const absoluteLocations = this . toAbsoluteLocations ( configuredLocations ) ;
167
+ return absoluteLocations . map ( firstNonGlobParentAndPattern ) ;
168
+ }
169
+
149
170
/**
150
171
* Converts locations defined in `settings` to absolute filesystem path URIs.
151
172
* This conversion is needed because locations in settings can be relative,
@@ -330,7 +351,7 @@ export const isValidGlob = (pattern: string): boolean => {
330
351
*/
331
352
const firstNonGlobParentAndPattern = (
332
353
location : URI
333
- ) : { parent : URI ; filePattern : string | undefined } => {
354
+ ) : { parent : URI ; filePattern ? : string } => {
334
355
const segments = location . path . split ( '/' ) ;
335
356
let i = 0 ;
336
357
while ( i < segments . length && isValidGlob ( segments [ i ] ) === false ) {
@@ -339,18 +360,16 @@ const firstNonGlobParentAndPattern = (
339
360
if ( i === segments . length ) {
340
361
// the path does not contain a glob pattern, so we can
341
362
// just find all prompt files in the provided location
342
- return { parent : location , filePattern : undefined } ;
363
+ return { parent : location } ;
343
364
}
365
+ const parent = location . with ( { path : segments . slice ( 0 , i ) . join ( '/' ) } ) ;
344
366
if ( i === segments . length - 1 && segments [ i ] === '*' || segments [ i ] === `` ) {
345
- return {
346
- parent : location . with ( { path : segments . slice ( 0 , i ) . join ( '/' ) } ) ,
347
- filePattern : undefined
348
- } ;
367
+ return { parent } ;
349
368
}
350
369
351
370
// the path contains a glob pattern, so we search in last folder that does not contain a glob pattern
352
371
return {
353
- parent : location . with ( { path : segments . slice ( 0 , i ) . join ( '/' ) } ) ,
372
+ parent,
354
373
filePattern : segments . slice ( i ) . join ( '/' )
355
374
} ;
356
375
} ;
0 commit comments