11
11
using System . Security ;
12
12
using System . Text ;
13
13
using System . Runtime . InteropServices ;
14
+ using Microsoft . Extensions . FileSystemGlobbing ;
15
+ using Microsoft . Extensions . FileSystemGlobbing . Abstractions ;
14
16
15
17
namespace Microsoft . PowerShell . EditorServices
16
18
{
@@ -22,11 +24,21 @@ public class Workspace
22
24
{
23
25
#region Private Fields
24
26
25
- private static readonly string [ ] s_psFilePatterns = new [ ]
27
+ private static readonly string [ ] s_psFileExtensions = new [ ]
26
28
{
27
- "*.ps1" ,
28
- "*.psm1" ,
29
- "*.psd1"
29
+ ".ps1" ,
30
+ ".psm1" ,
31
+ ".psd1"
32
+ } ;
33
+ // .Net Core doesn't appear to use the same three letter pattern matching rule although the docs
34
+ // suggest it should be find the '.ps1xml' files because we search for the pattern '*.ps1'
35
+ // ref https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.getfiles?view=netcore-2.1#System_IO_Directory_GetFiles_System_String_System_String_System_IO_EnumerationOptions_
36
+ private static readonly string [ ] s_psFileExtensionsDotNetFramework = new [ ]
37
+ {
38
+ ".ps1" ,
39
+ ".psm1" ,
40
+ ".psd1" ,
41
+ ".ps1xml"
30
42
} ;
31
43
32
44
private ILogger logger ;
@@ -282,135 +294,62 @@ public string GetRelativePath(string filePath)
282
294
}
283
295
284
296
/// <summary>
285
- /// Enumerate all the PowerShell (ps1, psm1, psd1) files in the workspace in a recursive manner
297
+ /// Whether the current runtime is Dot Net Framework as opposed to Core or Native
286
298
/// </summary>
287
- /// <returns>An enumerator over the PowerShell files found in the workspace</returns>
288
- public IEnumerable < string > EnumeratePSFiles ( )
299
+ private Boolean IsDotNetFrameWork ( )
289
300
{
290
- if ( WorkspacePath == null || ! Directory . Exists ( WorkspacePath ) )
291
- {
292
- return Enumerable . Empty < string > ( ) ;
293
- }
294
-
295
- var foundFiles = new List < string > ( ) ;
296
- this . RecursivelyEnumerateFiles ( WorkspacePath , ref foundFiles ) ;
297
- return foundFiles ;
301
+ return RuntimeInformation . FrameworkDescription . StartsWith ( ".NET Framework" ) ;
298
302
}
299
303
300
- #endregion
301
-
302
- #region Private Methods
303
-
304
304
/// <summary>
305
- /// Find PowerShell files recursively down from a given directory path.
306
- /// Currently collects files in depth-first order.
307
- /// Directory.GetFiles(folderPath, pattern, SearchOption.AllDirectories) would provide this,
308
- /// but a cycle in the filesystem will cause that to enter an infinite loop.
305
+ /// Enumerate all the PowerShell (ps1, psm1, psd1) files in the workspace in a recursive manner
309
306
/// </summary>
310
- /// <param name="folderPath">The path of the current directory to find files in</param>
311
- /// <param name="foundFiles">The accumulator for files found so far.</param>
312
- /// <param name="currDepth">The current depth of the recursion from the original base directory.</param>
313
- private void RecursivelyEnumerateFiles ( string folderPath , ref List < string > foundFiles , int currDepth = 0 )
307
+ /// <returns>An enumerator over the PowerShell files found in the workspace</returns>
308
+ public IEnumerable < string > EnumeratePSFiles (
309
+ string [ ] excludeGlobs = null ,
310
+ string [ ] includeGlobs = null ,
311
+ int maxDepth = 64 ,
312
+ Boolean ignoreReparsePoints = false
313
+ )
314
314
{
315
- const int recursionDepthLimit = 64 ;
316
-
317
- // Look for any PowerShell files in the current directory
318
- foreach ( string pattern in s_psFilePatterns )
319
- {
320
- string [ ] psFiles ;
321
- try
322
- {
323
- psFiles = Directory . GetFiles ( folderPath , pattern , SearchOption . TopDirectoryOnly ) ;
324
- }
325
- catch ( DirectoryNotFoundException e )
326
- {
327
- this . logger . WriteHandledException (
328
- $ "Could not enumerate files in the path '{ folderPath } ' due to it being an invalid path",
329
- e ) ;
330
-
331
- continue ;
332
- }
333
- catch ( PathTooLongException e )
334
- {
335
- this . logger . WriteHandledException (
336
- $ "Could not enumerate files in the path '{ folderPath } ' due to the path being too long",
337
- e ) ;
338
-
339
- continue ;
340
- }
341
- catch ( Exception e ) when ( e is SecurityException || e is UnauthorizedAccessException )
342
- {
343
- this . logger . WriteHandledException (
344
- $ "Could not enumerate files in the path '{ folderPath } ' due to the path not being accessible",
345
- e ) ;
346
-
347
- continue ;
348
- }
349
- catch ( Exception e )
350
- {
351
- this . logger . WriteHandledException (
352
- $ "Could not enumerate files in the path '{ folderPath } ' due to an exception",
353
- e ) ;
354
-
355
- continue ;
356
- }
357
-
358
- foundFiles . AddRange ( psFiles ) ;
359
- }
360
-
361
- // Prevent unbounded recursion here
362
- if ( currDepth >= recursionDepthLimit )
363
- {
364
- this . logger . Write ( LogLevel . Warning , $ "Recursion depth limit hit for path { folderPath } ") ;
365
- return ;
366
- }
367
-
368
- // Add the recursive directories to search next
369
- string [ ] subDirs ;
370
- try
315
+ if ( WorkspacePath == null || ! Directory . Exists ( WorkspacePath ) )
371
316
{
372
- subDirs = Directory . GetDirectories ( folderPath ) ;
317
+ yield break ;
373
318
}
374
- catch ( DirectoryNotFoundException e )
375
- {
376
- this . logger . WriteHandledException (
377
- $ "Could not enumerate directories in the path '{ folderPath } ' due to it being an invalid path",
378
- e ) ;
379
319
380
- return ;
381
- }
382
- catch ( PathTooLongException e )
320
+ var matcher = new Microsoft . Extensions . FileSystemGlobbing . Matcher ( ) ;
321
+ if ( includeGlobs == null || includeGlobs . Length == 0 )
383
322
{
384
- this . logger . WriteHandledException (
385
- $ "Could not enumerate directories in the path '{ folderPath } ' due to the path being too long",
386
- e ) ;
387
-
388
- return ;
323
+ // Include everything as the FileSystemWrapper will filter the
324
+ // files based on extension prematurely
325
+ matcher . AddInclude ( "**/*" ) ;
389
326
}
390
- catch ( Exception e ) when ( e is SecurityException || e is UnauthorizedAccessException )
327
+ else
391
328
{
392
- this . logger . WriteHandledException (
393
- $ "Could not enumerate directories in the path '{ folderPath } ' due to the path not being accessible",
394
- e ) ;
395
-
396
- return ;
329
+ foreach ( string pattern in includeGlobs ) { matcher . AddInclude ( pattern ) ; }
397
330
}
398
- catch ( Exception e )
331
+ if ( excludeGlobs != null )
399
332
{
400
- this . logger . WriteHandledException (
401
- $ "Could not enumerate directories in the path '{ folderPath } ' due to an exception",
402
- e ) ;
403
-
404
- return ;
333
+ foreach ( string pattern in excludeGlobs ) { matcher . AddExclude ( pattern ) ; }
405
334
}
406
335
407
-
408
- foreach ( string subDir in subDirs )
336
+ var fsFactory = new WorkspaceFileSystemWrapperFactory (
337
+ WorkspacePath ,
338
+ maxDepth ,
339
+ this . IsDotNetFrameWork ( ) ? s_psFileExtensionsDotNetFramework : s_psFileExtensions ,
340
+ ignoreReparsePoints ,
341
+ this . logger
342
+ ) ;
343
+ var result = matcher . Execute ( fsFactory . RootDirectory ) ;
344
+ foreach ( FilePatternMatch item in result . Files )
409
345
{
410
- RecursivelyEnumerateFiles ( subDir , ref foundFiles , currDepth : currDepth + 1 ) ;
346
+ yield return Path . Combine ( WorkspacePath , item . Path . Replace ( '/' , Path . DirectorySeparatorChar ) ) ;
411
347
}
412
348
}
413
349
350
+ #endregion
351
+
352
+ #region Private Methods
414
353
/// <summary>
415
354
/// Recusrively searches through referencedFiles in scriptFiles
416
355
/// and builds a Dictonary of the file references
0 commit comments