@@ -207,6 +207,114 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
207
207
return result ;
208
208
}
209
209
210
+ // Given a zero based offset, find the line text preceeding it in the document
211
+ private preceedingText ( offset : number ,
212
+ document : vscode . TextDocument ) : string {
213
+ const endPos = document . positionAt ( offset ) ;
214
+ const startPos = endPos . translate ( 0 , - endPos . character ) ;
215
+
216
+ return document . getText ( new vscode . Range ( startPos , endPos ) ) ;
217
+ }
218
+
219
+ // Given a zero based offset, return the line number in the document
220
+ private lineAtOffest ( offset : number ,
221
+ document : vscode . TextDocument ) : number {
222
+ return document . positionAt ( offset ) . line ;
223
+ }
224
+
225
+ // Finding blocks of comment tokens is more complicated as the newline characters are not
226
+ // classed as comments. To workaround this we search for the comment character `#` scope name
227
+ // "punctuation.definition.comment.powershell" and then determine contiguous line numbers from there
228
+ private matchBlockCommentScopeElements ( tokens ,
229
+ document : vscode . TextDocument ) {
230
+ const result = [ ] ;
231
+
232
+ const emptyLine = new RegExp ( "^[\\s]+$" ) ;
233
+
234
+ let startLine : number = - 1 ;
235
+ let nextLine : number = - 1 ;
236
+
237
+ tokens . forEach ( ( token , index ) => {
238
+ if ( token . scopes . includes ( "punctuation.definition.comment.powershell" ) ) {
239
+ // The punctuation.definition.comment.powershell token matches new-line comments
240
+ // and inline comments e.g. `$x = 'foo' # inline comment`. We are only interested
241
+ // in comments which begin the line i.e. no preceeding text
242
+ if ( emptyLine . test ( this . preceedingText ( token . startIndex , document ) ) ) {
243
+ const lineNum = this . lineAtOffest ( token . startIndex , document ) ;
244
+ // A simple pattern for keeping track of contiguous numbers in a known
245
+ // sorted array
246
+ if ( startLine === - 1 ) {
247
+ startLine = lineNum ;
248
+ nextLine = lineNum + 1 ;
249
+ } else {
250
+ if ( lineNum === nextLine ) {
251
+ nextLine = lineNum + 1 ;
252
+ } else {
253
+ result . push ( new MatchedToken ( null ,
254
+ null ,
255
+ startLine ,
256
+ nextLine - 1 ,
257
+ MatchType . Comment ,
258
+ document ) ) ;
259
+ startLine = lineNum ;
260
+ nextLine = lineNum + 1 ;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ } ) ;
266
+
267
+ // If we exist the token array and we're still processing comment lines, then the
268
+ // comment block simply ends at the end of document
269
+ if ( startLine !== - 1 ) {
270
+ result . push ( new MatchedToken ( null , null , startLine , nextLine - 1 , MatchType . Comment , document ) ) ;
271
+ }
272
+
273
+ return result ;
274
+ }
275
+
276
+ // Given a zero based offset, find the line text after it in the document
277
+ private subsequentText ( offset : number ,
278
+ document : vscode . TextDocument ) : string {
279
+ const startPos = document . positionAt ( offset ) ;
280
+ // We don't know how long the line is so just return a really long one.
281
+ const endPos = startPos . translate ( 0 , 1000 ) ;
282
+ return document . getText ( new vscode . Range ( startPos , endPos ) ) ;
283
+ }
284
+
285
+ // Create a new token object with an appended scopeName
286
+ private addTokenScope ( token ,
287
+ scopeName : string ) {
288
+ // Only a shallow clone is required
289
+ const tokenClone = Object . assign ( { } , token ) ;
290
+ tokenClone . scopes . push ( scopeName ) ;
291
+ return tokenClone ;
292
+ }
293
+
294
+ // Given a list of grammar tokens, find the tokens that are comments and
295
+ // the comment text is either `# region` or `# endregion`. Return a new list of tokens
296
+ // with custom scope names added, "custom.start.region" and "custom.end.region" respectively
297
+ private extractRegionScopeElements ( tokens ,
298
+ document : vscode . TextDocument ) {
299
+ const result = [ ] ;
300
+
301
+ const emptyLine = new RegExp ( "^[\\s]+$" ) ;
302
+ const startRegionText = new RegExp ( "^#\\s*region\\b" ) ;
303
+ const endRegionText = new RegExp ( "^#\\s*endregion\\b" ) ;
304
+
305
+ tokens . forEach ( ( token , index ) => {
306
+ if ( token . scopes . includes ( "punctuation.definition.comment.powershell" ) ) {
307
+ if ( emptyLine . test ( this . preceedingText ( token . startIndex , document ) ) ) {
308
+ const commentText = this . subsequentText ( token . startIndex , document ) ;
309
+
310
+ if ( startRegionText . test ( commentText ) ) { result . push ( this . addTokenScope ( token , "custom.start.region" ) ) ; }
311
+ if ( endRegionText . test ( commentText ) ) { result . push ( this . addTokenScope ( token , "custom.end.region" ) ) ; }
312
+ }
313
+ }
314
+ } ) ;
315
+ return result ;
316
+ }
317
+
210
318
// Given a list of tokens, return a list of matched tokens/line numbers regions
211
319
private matchGrammarTokens ( tokens , document : vscode . TextDocument ) : IMatchedTokenList {
212
320
const matchedTokens = [ ] ;
@@ -237,6 +345,23 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
237
345
MatchType . Region , document )
238
346
. forEach ( ( x ) => { matchedTokens . push ( x ) ; } ) ;
239
347
348
+ // Find matching Braces #region -> #endregion
349
+ this . matchScopeElements ( this . extractRegionScopeElements ( tokens , document ) ,
350
+ "custom.start.region" ,
351
+ "custom.end.region" ,
352
+ MatchType . Region , document )
353
+ . forEach ( ( x ) => { matchedTokens . push ( x ) ; } ) ;
354
+
355
+ // Find blocks of line comments # comment1\n# comment2\n...
356
+ this . matchBlockCommentScopeElements ( tokens , document ) . forEach ( ( x ) => { matchedTokens . push ( x ) ; } ) ;
357
+
358
+ // Find matching function definitions <# -> #>
359
+ this . matchScopeElements ( tokens ,
360
+ "punctuation.definition.comment.block.begin.powershell" ,
361
+ "punctuation.definition.comment.block.end.powershell" ,
362
+ MatchType . Comment , document )
363
+ . forEach ( ( x ) => { matchedTokens . push ( x ) ; } ) ;
364
+
240
365
return matchedTokens ;
241
366
}
242
367
0 commit comments