3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
import { CancellationToken } from 'vs/base/common/cancellation' ;
6
+ import * as glob from 'vs/base/common/glob' ;
6
7
import { ResourceSet , ResourceMap } from 'vs/base/common/map' ;
7
8
import { URI } from 'vs/base/common/uri' ;
8
9
import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
@@ -12,13 +13,16 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note
12
13
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService' ;
13
14
import { INotebookSearchService } from 'vs/workbench/contrib/search/common/notebookSearch' ;
14
15
import { INotebookCellMatchWithModel , INotebookFileMatchWithModel , contentMatchesToTextSearchMatches , webviewMatchesToTextSearchMatches } from 'vs/workbench/contrib/search/browser/notebookSearch/searchNotebookHelpers' ;
15
- import { ITextQuery , QueryType , ISearchProgressItem , ISearchComplete , ISearchConfigurationProperties , pathIncludedInQuery } from 'vs/workbench/services/search/common/search' ;
16
+ import { ITextQuery , QueryType , ISearchProgressItem , ISearchComplete , ISearchConfigurationProperties , pathIncludedInQuery , ISearchService , IFolderQuery } from 'vs/workbench/services/search/common/search' ;
16
17
import * as arrays from 'vs/base/common/arrays' ;
17
18
import { isNumber } from 'vs/base/common/types' ;
18
19
import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService' ;
19
20
import { INotebookFileMatchNoModel } from 'vs/workbench/contrib/search/common/searchNotebookHelpers' ;
20
21
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService' ;
21
22
import { NotebookPriorityInfo } from 'vs/workbench/contrib/search/common/search' ;
23
+ import { INotebookExclusiveDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon' ;
24
+ import { QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder' ;
25
+ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
22
26
23
27
interface IOpenNotebookSearchResults {
24
28
results : ResourceMap < INotebookFileMatchWithModel | null > ;
@@ -30,17 +34,20 @@ interface IClosedNotebookSearchResults {
30
34
}
31
35
export class NotebookSearchService implements INotebookSearchService {
32
36
declare readonly _serviceBrand : undefined ;
37
+ private queryBuilder : QueryBuilder ;
33
38
constructor (
34
39
@IUriIdentityService private readonly uriIdentityService : IUriIdentityService ,
35
40
@INotebookEditorService private readonly notebookEditorService : INotebookEditorService ,
36
41
@ILogService private readonly logService : ILogService ,
37
42
@INotebookService private readonly notebookService : INotebookService ,
38
43
@IConfigurationService private readonly configurationService : IConfigurationService ,
39
- @IEditorResolverService private readonly editorResolverService : IEditorResolverService
44
+ @IEditorResolverService private readonly editorResolverService : IEditorResolverService ,
45
+ @ISearchService private readonly searchService : ISearchService ,
46
+ @IInstantiationService instantiationService : IInstantiationService
40
47
) {
48
+ this . queryBuilder = instantiationService . createInstance ( QueryBuilder ) ;
41
49
}
42
50
43
-
44
51
notebookSearch ( query : ITextQuery , token : CancellationToken | undefined , searchInstanceID : string , onProgress ?: ( result : ISearchProgressItem ) => void ) : {
45
52
openFilesToScan : ResourceSet ;
46
53
completeData : Promise < ISearchComplete > ;
@@ -109,14 +116,38 @@ export class NotebookSearchService implements INotebookSearchService {
109
116
} ;
110
117
}
111
118
119
+ private async doesFileExist ( includes : string [ ] , folderQueries : IFolderQuery < URI > [ ] , token : CancellationToken ) : Promise < boolean > {
120
+ const promises : Promise < void > [ ] = includes . map ( async includePattern => {
121
+ const query = this . queryBuilder . file ( folderQueries . map ( e => e . folder ) , {
122
+ includePattern : includePattern . startsWith ( '/' ) ? includePattern : '**/' + includePattern , // todo: find cleaner way to ensure that globs match all appropriate filetypes
123
+ exists : true
124
+ } ) ;
125
+ return this . searchService . fileSearch (
126
+ query ,
127
+ token
128
+ ) . then ( ( ret ) => {
129
+ if ( ! ret . limitHit ) {
130
+ throw Error ( 'File not found' ) ;
131
+ }
132
+ } ) ;
133
+ } ) ;
134
+
135
+ return Promise . any ( promises ) . then ( ( ) => true ) . catch ( ( ) => false ) ;
136
+ }
137
+
112
138
private async getClosedNotebookResults ( textQuery : ITextQuery , scannedFiles : ResourceSet , token : CancellationToken ) : Promise < IClosedNotebookSearchResults > {
113
139
114
140
const userAssociations = this . editorResolverService . getAllUserAssociations ( ) ;
115
141
const allPriorityInfo : Map < string , NotebookPriorityInfo [ ] > = new Map ( ) ;
116
142
const contributedNotebookTypes = this . notebookService . getContributedNotebookTypes ( ) ;
117
143
144
+
118
145
userAssociations . forEach ( association => {
119
146
147
+ // we gather the editor associations here, but cannot check them until we actually have the files that the glob matches
148
+ // this is because longer patterns take precedence over shorter ones, and even if there is a user association that
149
+ // specifies the exact same glob as a contributed notebook type, there might be another user association that is longer/more specific
150
+ // that still matches the path and should therefore take more precedence.
120
151
if ( ! association . filenamePattern ) {
121
152
return ;
122
153
}
@@ -140,14 +171,26 @@ export class NotebookSearchService implements INotebookSearchService {
140
171
} | undefined > [ ] = [ ] ;
141
172
142
173
contributedNotebookTypes . forEach ( ( notebook ) => {
143
- promises . push ( ( async ( ) => {
144
- const canResolve = await this . notebookService . canResolve ( notebook . id ) ;
145
- if ( ! canResolve ) {
146
- return undefined ;
147
- }
148
- const serializer = ( await this . notebookService . withNotebookDataProvider ( notebook . id ) ) . serializer ;
149
- return await serializer . searchInNotebooks ( textQuery , token , allPriorityInfo ) ;
150
- } ) ( ) ) ;
174
+ if ( notebook . selectors . length > 0 ) {
175
+ promises . push ( ( async ( ) => {
176
+ const includes = notebook . selectors . map ( ( selector ) => {
177
+ const globPattern = ( selector as INotebookExclusiveDocumentFilter ) . include || selector as glob . IRelativePattern | string ;
178
+ return globPattern . toString ( ) ;
179
+ } ) ;
180
+
181
+ const isInWorkspace = await this . doesFileExist ( includes , textQuery . folderQueries , token ) ;
182
+ if ( isInWorkspace ) {
183
+ const canResolve = await this . notebookService . canResolve ( notebook . id ) ;
184
+ if ( ! canResolve ) {
185
+ return undefined ;
186
+ }
187
+ const serializer = ( await this . notebookService . withNotebookDataProvider ( notebook . id ) ) . serializer ;
188
+ return await serializer . searchInNotebooks ( textQuery , token , allPriorityInfo ) ;
189
+ } else {
190
+ return undefined ;
191
+ }
192
+ } ) ( ) ) ;
193
+ }
151
194
} ) ;
152
195
153
196
const start = Date . now ( ) ;
0 commit comments