33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { Match , searchMatchComparer , FileMatch , SearchResult } from 'vs/workbench/contrib/search/common/searchModel' ;
6+ import { Match , searchMatchComparer , FileMatch , SearchResult , SearchModel } from 'vs/workbench/contrib/search/common/searchModel' ;
77import { repeat } from 'vs/base/common/strings' ;
88import { ILabelService } from 'vs/platform/label/common/label' ;
99import { coalesce , flatten } from 'vs/base/common/arrays' ;
1010import { IEditorService } from 'vs/workbench/services/editor/common/editorService' ;
1111import { URI } from 'vs/base/common/uri' ;
12- import { ITextQuery } from 'vs/workbench/services/search/common/search' ;
12+ import { ITextQuery , IPatternInfo , ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search' ;
1313import * as network from 'vs/base/common/network' ;
1414import { Range } from 'vs/editor/common/core/range' ;
1515import { ITextModel , TrackedRangeStickiness } from 'vs/editor/common/model' ;
16+ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
17+ import { ITextQueryBuilderOptions , QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder' ;
18+ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search' ;
19+ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace' ;
20+ import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
1621
1722// Using \r\n on Windows inserts an extra newline between results.
1823const lineDelimiter = '\n' ;
1924
20- const translateRangeLines = ( n : number ) => ( range : Range ) => new Range ( range . startLineNumber + n , range . startColumn , range . endLineNumber + n , range . endColumn ) ;
25+ const translateRangeLines =
26+ ( n : number ) =>
27+ ( range : Range ) =>
28+ new Range ( range . startLineNumber + n , range . startColumn , range . endLineNumber + n , range . endColumn ) ;
2129
22- type SearchResultSerialization = { text : string [ ] , matchRanges : Range [ ] } ;
23-
24- function matchToSearchResultFormat ( match : Match ) : { line : string , ranges : Range [ ] , lineNumber : string } [ ] {
30+ const matchToSearchResultFormat = ( match : Match ) : { line : string , ranges : Range [ ] , lineNumber : string } [ ] => {
2531 const getLinePrefix = ( i : number ) => `${ match . range ( ) . startLineNumber + i } ` ;
2632
2733 const fullMatchLines = match . fullPreviewLines ( ) ;
@@ -54,8 +60,9 @@ function matchToSearchResultFormat(match: Match): { line: string, ranges: Range[
5460 } ) ;
5561
5662 return results ;
57- }
63+ } ;
5864
65+ type SearchResultSerialization = { text : string [ ] , matchRanges : Range [ ] } ;
5966function fileMatchToSearchResultFormat ( fileMatch : FileMatch , labelFormatter : ( x : URI ) => string ) : SearchResultSerialization {
6067 const serializedMatches = flatten ( fileMatch . matches ( )
6168 . sort ( searchMatchComparer )
@@ -95,7 +102,7 @@ const flattenSearchResultSerializations = (serializations: SearchResultSerializa
95102 return { text, matchRanges } ;
96103} ;
97104
98- function contentPatternToSearchResultHeader ( pattern : ITextQuery | null , includes : string , excludes : string ) : string [ ] {
105+ const contentPatternToSearchResultHeader = ( pattern : ITextQuery | null , includes : string , excludes : string ) : string [ ] => {
99106 if ( ! pattern ) { return [ ] ; }
100107
101108 const removeNullFalseAndUndefined = < T > ( a : ( T | null | false | undefined ) [ ] ) => a . filter ( a => a !== false && a !== null && a !== undefined ) as T [ ] ;
@@ -105,29 +112,119 @@ function contentPatternToSearchResultHeader(pattern: ITextQuery | null, includes
105112 return removeNullFalseAndUndefined ( [
106113 `# Query: ${ escapeNewlines ( pattern . contentPattern . pattern ) } ` ,
107114
108- ( pattern . contentPattern . isCaseSensitive || pattern . contentPattern . isWordMatch || pattern . contentPattern . isRegExp )
115+ ( pattern . contentPattern . isCaseSensitive || pattern . contentPattern . isWordMatch || pattern . contentPattern . isRegExp || pattern . userDisabledExcludesAndIgnoreFiles )
109116 && `# Flags: ${ coalesce ( [
110117 pattern . contentPattern . isCaseSensitive && 'CaseSensitive' ,
111118 pattern . contentPattern . isWordMatch && 'WordMatch' ,
112- pattern . contentPattern . isRegExp && 'RegExp'
119+ pattern . contentPattern . isRegExp && 'RegExp' ,
120+ pattern . userDisabledExcludesAndIgnoreFiles && 'IgnoreExcludeSettings'
113121 ] ) . join ( ' ' ) } `,
114122 includes ? `# Including: ${ includes } ` : undefined ,
115123 excludes ? `# Excluding: ${ excludes } ` : undefined ,
116124 ''
117125 ] ) ;
118- }
126+ } ;
127+
128+ const searchHeaderToContentPattern = ( header : string [ ] ) : { pattern : string , flags : { regex : boolean , wholeWord : boolean , caseSensitive : boolean , ignoreExcludes : boolean } , includes : string , excludes : string } => {
129+ const query = {
130+ pattern : '' ,
131+ flags : { regex : false , caseSensitive : false , ignoreExcludes : false , wholeWord : false } ,
132+ includes : '' ,
133+ excludes : ''
134+ } ;
135+
136+ const unescapeNewlines = ( str : string ) => str . replace ( / \\ \\ / g, '\\' ) . replace ( / \\ n / g, '\n' ) ;
137+ const parseYML = / ^ # ( [ ^ : ] * ) : ( .* ) $ / ;
138+ for ( const line of header ) {
139+ const parsed = parseYML . exec ( line ) ;
140+ if ( ! parsed ) { continue ; }
141+ const [ , key , value ] = parsed ;
142+ switch ( key ) {
143+ case 'Query' : query . pattern = unescapeNewlines ( value ) ; break ;
144+ case 'Including' : query . includes = value ; break ;
145+ case 'Excluding' : query . excludes = value ; break ;
146+ case 'Flags' : {
147+ query . flags = {
148+ regex : value . indexOf ( 'RegExp' ) !== - 1 ,
149+ caseSensitive : value . indexOf ( 'CaseSensitive' ) !== - 1 ,
150+ ignoreExcludes : value . indexOf ( 'IgnoreExcludeSettings' ) !== - 1 ,
151+ wholeWord : value . indexOf ( 'WordMatch' ) !== - 1
152+ } ;
153+ }
154+ }
155+ }
156+
157+ return query ;
158+ } ;
119159
120160const serializeSearchResultForEditor = ( searchResult : SearchResult , rawIncludePattern : string , rawExcludePattern : string , labelFormatter : ( x : URI ) => string ) : SearchResultSerialization => {
121161 const header = contentPatternToSearchResultHeader ( searchResult . query , rawIncludePattern , rawExcludePattern ) ;
122162 const allResults =
123163 flattenSearchResultSerializations (
124- flatten ( searchResult . folderMatches ( )
125- . map ( folderMatch => folderMatch . matches ( )
164+ flatten ( searchResult . folderMatches ( ) . sort ( searchMatchComparer )
165+ . map ( folderMatch => folderMatch . matches ( ) . sort ( searchMatchComparer )
126166 . map ( fileMatch => fileMatchToSearchResultFormat ( fileMatch , labelFormatter ) ) ) ) ) ;
127167
128168 return { matchRanges : allResults . matchRanges . map ( translateRangeLines ( header . length ) ) , text : header . concat ( allResults . text ) } ;
129169} ;
130170
171+ export const refreshActiveEditorSearch =
172+ async ( editorService : IEditorService , instantiationService : IInstantiationService , contextService : IWorkspaceContextService , labelService : ILabelService , configurationService : IConfigurationService ) => {
173+ const model = editorService . activeTextEditorWidget ?. getModel ( ) ;
174+ if ( ! model ) { return ; }
175+
176+ const textModel = model as ITextModel ;
177+
178+ const header = textModel . getValueInRange ( new Range ( 1 , 1 , 5 , 1 ) )
179+ . split ( lineDelimiter )
180+ . filter ( line => line . indexOf ( '# ' ) === 0 ) ;
181+
182+ const contentPattern = searchHeaderToContentPattern ( header ) ;
183+
184+ const content : IPatternInfo = {
185+ pattern : contentPattern . pattern ,
186+ isRegExp : contentPattern . flags . regex ,
187+ isCaseSensitive : contentPattern . flags . caseSensitive ,
188+ isWordMatch : contentPattern . flags . wholeWord
189+ } ;
190+
191+ const options : ITextQueryBuilderOptions = {
192+ _reason : 'searchEditor' ,
193+ extraFileResources : instantiationService . invokeFunction ( getOutOfWorkspaceEditorResources ) ,
194+ maxResults : 10000 ,
195+ disregardIgnoreFiles : contentPattern . flags . ignoreExcludes ,
196+ disregardExcludeSettings : contentPattern . flags . ignoreExcludes ,
197+ excludePattern : contentPattern . excludes ,
198+ includePattern : contentPattern . includes ,
199+ previewOptions : {
200+ matchLines : 1 ,
201+ charsPerLine : 1000
202+ } ,
203+ isSmartCase : configurationService . getValue < ISearchConfigurationProperties > ( 'search' ) . smartCase ,
204+ expandPatterns : true
205+ } ;
206+
207+ const folderResources = contextService . getWorkspace ( ) . folders ;
208+
209+ let query : ITextQuery ;
210+ try {
211+ const queryBuilder = instantiationService . createInstance ( QueryBuilder ) ;
212+ query = queryBuilder . text ( content , folderResources . map ( folder => folder . uri ) , options ) ;
213+ } catch ( err ) {
214+ return ;
215+ }
216+
217+ const searchModel = instantiationService . createInstance ( SearchModel ) ;
218+ await searchModel . search ( query ) ;
219+
220+ const labelFormatter = ( uri : URI ) : string => labelService . getUriLabel ( uri , { relative : true } ) ;
221+ const results = serializeSearchResultForEditor ( searchModel . searchResult , '' , '' , labelFormatter ) ;
222+
223+ textModel . setValue ( results . text . join ( lineDelimiter ) ) ;
224+ textModel . deltaDecorations ( [ ] , results . matchRanges . map ( range => ( { range, options : { className : 'findMatch' , stickiness : TrackedRangeStickiness . NeverGrowsWhenTypingAtEdges } } ) ) ) ;
225+ } ;
226+
227+
131228export const createEditorFromSearchResult =
132229 async ( searchResult : SearchResult , rawIncludePattern : string , rawExcludePattern : string , labelService : ILabelService , editorService : IEditorService ) => {
133230 const searchTerm = searchResult . query ?. contentPattern . pattern . replace ( / [ ^ \w - _ . ] / g, '' ) || 'Search' ;
@@ -154,5 +251,4 @@ export const createEditorFromSearchResult =
154251 const model = control . getModel ( ) as ITextModel ;
155252
156253 model . deltaDecorations ( [ ] , results . matchRanges . map ( range => ( { range, options : { className : 'findMatch' , stickiness : TrackedRangeStickiness . NeverGrowsWhenTypingAtEdges } } ) ) ) ;
157-
158254 } ;
0 commit comments