@@ -65,6 +65,7 @@ const matchToSearchResultFormat = (match: Match): { line: string, ranges: Range[
6565} ;
6666
6767type SearchResultSerialization = { text : string [ ] , matchRanges : Range [ ] } ;
68+
6869function fileMatchToSearchResultFormat ( fileMatch : FileMatch , labelFormatter : ( x : URI ) => string ) : SearchResultSerialization {
6970 const serializedMatches = flatten ( fileMatch . matches ( )
7071 . sort ( searchMatchComparer )
@@ -76,17 +77,37 @@ function fileMatchToSearchResultFormat(fileMatch: FileMatch, labelFormatter: (x:
7677
7778 const targetLineNumberToOffset : Record < string , number > = { } ;
7879
80+ const context : { line : string , lineNumber : number } [ ] = [ ] ;
81+ fileMatch . context . forEach ( ( line , lineNumber ) => context . push ( { line, lineNumber } ) ) ;
82+ context . sort ( ( a , b ) => a . lineNumber - b . lineNumber ) ;
83+
84+ let lastLine : number | undefined = undefined ;
85+
7986 const seenLines = new Set < string > ( ) ;
8087 serializedMatches . forEach ( match => {
8188 if ( ! seenLines . has ( match . line ) ) {
89+ while ( context . length && context [ 0 ] . lineNumber < + match . lineNumber ) {
90+ const { line, lineNumber } = context . shift ( ) ! ;
91+ if ( lastLine !== undefined && lineNumber !== lastLine + 1 ) {
92+ text . push ( '' ) ;
93+ }
94+ text . push ( ` ${ lineNumber } ${ line } ` ) ;
95+ lastLine = lineNumber ;
96+ }
97+
8298 targetLineNumberToOffset [ match . lineNumber ] = text . length ;
8399 seenLines . add ( match . line ) ;
84100 text . push ( match . line ) ;
101+ lastLine = + match . lineNumber ;
85102 }
86103
87104 matchRanges . push ( ...match . ranges . map ( translateRangeLines ( targetLineNumberToOffset [ match . lineNumber ] ) ) ) ;
88105 } ) ;
89106
107+ while ( context . length ) {
108+ const { line, lineNumber } = context . shift ( ) ! ;
109+ text . push ( ` ${ lineNumber } ${ line } ` ) ;
110+ }
90111
91112 return { text, matchRanges } ;
92113}
@@ -104,7 +125,7 @@ const flattenSearchResultSerializations = (serializations: SearchResultSerializa
104125 return { text, matchRanges } ;
105126} ;
106127
107- const contentPatternToSearchResultHeader = ( pattern : ITextQuery | null , includes : string , excludes : string ) : string [ ] => {
128+ const contentPatternToSearchResultHeader = ( pattern : ITextQuery | null , includes : string , excludes : string , contextLines : number ) : string [ ] => {
108129 if ( ! pattern ) { return [ ] ; }
109130
110131 const removeNullFalseAndUndefined = < T > ( a : ( T | null | false | undefined ) [ ] ) => a . filter ( a => a !== false && a !== null && a !== undefined ) as T [ ] ;
@@ -123,16 +144,32 @@ const contentPatternToSearchResultHeader = (pattern: ITextQuery | null, includes
123144 ] ) . join ( ' ' ) } `,
124145 includes ? `# Including: ${ includes } ` : undefined ,
125146 excludes ? `# Excluding: ${ excludes } ` : undefined ,
147+ contextLines ? `# ContextLines: ${ contextLines } ` : undefined ,
126148 ''
127149 ] ) ;
128150} ;
129151
130- const searchHeaderToContentPattern = ( header : string [ ] ) : { pattern : string , flags : { regex : boolean , wholeWord : boolean , caseSensitive : boolean , ignoreExcludes : boolean } , includes : string , excludes : string } => {
131- const query = {
152+
153+ type SearchHeader = {
154+ pattern : string ;
155+ flags : {
156+ regex : boolean ;
157+ wholeWord : boolean ;
158+ caseSensitive : boolean ;
159+ ignoreExcludes : boolean ;
160+ } ;
161+ includes : string ;
162+ excludes : string ;
163+ context : number | undefined ;
164+ } ;
165+
166+ const searchHeaderToContentPattern = ( header : string [ ] ) : SearchHeader => {
167+ const query : SearchHeader = {
132168 pattern : '' ,
133169 flags : { regex : false , caseSensitive : false , ignoreExcludes : false , wholeWord : false } ,
134170 includes : '' ,
135- excludes : ''
171+ excludes : '' ,
172+ context : undefined
136173 } ;
137174
138175 const unescapeNewlines = ( str : string ) => str . replace ( / \\ \\ / g, '\\' ) . replace ( / \\ n / g, '\n' ) ;
@@ -145,6 +182,7 @@ const searchHeaderToContentPattern = (header: string[]): { pattern: string, flag
145182 case 'Query' : query . pattern = unescapeNewlines ( value ) ; break ;
146183 case 'Including' : query . includes = value ; break ;
147184 case 'Excluding' : query . excludes = value ; break ;
185+ case 'ContextLines' : query . context = + value ; break ;
148186 case 'Flags' : {
149187 query . flags = {
150188 regex : value . indexOf ( 'RegExp' ) !== - 1 ,
@@ -159,19 +197,20 @@ const searchHeaderToContentPattern = (header: string[]): { pattern: string, flag
159197 return query ;
160198} ;
161199
162- const serializeSearchResultForEditor = ( searchResult : SearchResult , rawIncludePattern : string , rawExcludePattern : string , labelFormatter : ( x : URI ) => string ) : SearchResultSerialization => {
163- const header = contentPatternToSearchResultHeader ( searchResult . query , rawIncludePattern , rawExcludePattern ) ;
200+ const serializeSearchResultForEditor = ( searchResult : SearchResult , rawIncludePattern : string , rawExcludePattern : string , contextLines : number , labelFormatter : ( x : URI ) => string ) : SearchResultSerialization => {
201+ const header = contentPatternToSearchResultHeader ( searchResult . query , rawIncludePattern , rawExcludePattern , contextLines ) ;
164202 const allResults =
165203 flattenSearchResultSerializations (
166- flatten ( searchResult . folderMatches ( ) . sort ( searchMatchComparer )
167- . map ( folderMatch => folderMatch . matches ( ) . sort ( searchMatchComparer )
168- . map ( fileMatch => fileMatchToSearchResultFormat ( fileMatch , labelFormatter ) ) ) ) ) ;
204+ flatten (
205+ searchResult . folderMatches ( ) . sort ( searchMatchComparer )
206+ . map ( folderMatch => folderMatch . matches ( ) . sort ( searchMatchComparer )
207+ . map ( fileMatch => fileMatchToSearchResultFormat ( fileMatch , labelFormatter ) ) ) ) ) ;
169208
170209 return { matchRanges : allResults . matchRanges . map ( translateRangeLines ( header . length ) ) , text : header . concat ( allResults . text ) } ;
171210} ;
172211
173212export const refreshActiveEditorSearch =
174- async ( editorService : IEditorService , instantiationService : IInstantiationService , contextService : IWorkspaceContextService , labelService : ILabelService , configurationService : IConfigurationService ) => {
213+ async ( contextLines : number | undefined , editorService : IEditorService , instantiationService : IInstantiationService , contextService : IWorkspaceContextService , labelService : ILabelService , configurationService : IConfigurationService ) => {
175214 const model = editorService . activeTextEditorWidget ?. getModel ( ) ;
176215 if ( ! model ) { return ; }
177216
@@ -190,6 +229,8 @@ export const refreshActiveEditorSearch =
190229 isWordMatch : contentPattern . flags . wholeWord
191230 } ;
192231
232+ contextLines = contextLines ?? contentPattern . context ?? 0 ;
233+
193234 const options : ITextQueryBuilderOptions = {
194235 _reason : 'searchEditor' ,
195236 extraFileResources : instantiationService . invokeFunction ( getOutOfWorkspaceEditorResources ) ,
@@ -202,6 +243,8 @@ export const refreshActiveEditorSearch =
202243 matchLines : 1 ,
203244 charsPerLine : 1000
204245 } ,
246+ afterContext : contextLines ,
247+ beforeContext : contextLines ,
205248 isSmartCase : configurationService . getValue < ISearchConfigurationProperties > ( 'search' ) . smartCase ,
206249 expandPatterns : true
207250 } ;
@@ -220,7 +263,7 @@ export const refreshActiveEditorSearch =
220263 await searchModel . search ( query ) ;
221264
222265 const labelFormatter = ( uri : URI ) : string => labelService . getUriLabel ( uri , { relative : true } ) ;
223- const results = serializeSearchResultForEditor ( searchModel . searchResult , '' , '' , labelFormatter ) ;
266+ const results = serializeSearchResultForEditor ( searchModel . searchResult , contentPattern . includes , contentPattern . excludes , contextLines , labelFormatter ) ;
224267
225268 textModel . setValue ( results . text . join ( lineDelimiter ) ) ;
226269 textModel . deltaDecorations ( [ ] , results . matchRanges . map ( range => ( { range, options : { className : 'searchEditorFindMatch' , stickiness : TrackedRangeStickiness . NeverGrowsWhenTypingAtEdges } } ) ) ) ;
@@ -233,7 +276,7 @@ export const createEditorFromSearchResult =
233276
234277 const labelFormatter = ( uri : URI ) : string => labelService . getUriLabel ( uri , { relative : true } ) ;
235278
236- const results = serializeSearchResultForEditor ( searchResult , rawIncludePattern , rawExcludePattern , labelFormatter ) ;
279+ const results = serializeSearchResultForEditor ( searchResult , rawIncludePattern , rawExcludePattern , 0 , labelFormatter ) ;
237280
238281 let possible = {
239282 contents : results . text . join ( lineDelimiter ) ,
@@ -255,7 +298,6 @@ export const createEditorFromSearchResult =
255298 model . deltaDecorations ( [ ] , results . matchRanges . map ( range => ( { range, options : { className : 'searchEditorFindMatch' , stickiness : TrackedRangeStickiness . NeverGrowsWhenTypingAtEdges } } ) ) ) ;
256299 } ;
257300
258- // theming
259301registerThemingParticipant ( ( theme , collector ) => {
260302 collector . addRule ( `.monaco-editor .searchEditorFindMatch { background-color: ${ theme . getColor ( searchEditorFindMatch ) } ; }` ) ;
261303
0 commit comments