6
6
using System . Collections . Generic ;
7
7
using System . IO ;
8
8
using System . Linq ;
9
+ using System . Management . Automation ;
9
10
using System . Security ;
10
11
using System . Text ;
11
- using Microsoft . Extensions . FileSystemGlobbing ;
12
+ using System . Threading ;
12
13
using Microsoft . Extensions . Logging ;
14
+ using Microsoft . PowerShell . EditorServices . Services . PowerShell ;
13
15
using Microsoft . PowerShell . EditorServices . Services . TextDocument ;
14
- using Microsoft . PowerShell . EditorServices . Services . Workspace ;
15
16
using Microsoft . PowerShell . EditorServices . Utility ;
16
17
using OmniSharp . Extensions . LanguageServer . Protocol ;
17
18
using OmniSharp . Extensions . LanguageServer . Protocol . Models ;
@@ -84,20 +85,23 @@ internal class WorkspaceService
84
85
/// </summary>
85
86
public bool FollowSymlinks { get ; set ; }
86
87
88
+ private readonly IInternalPowerShellExecutionService executionService ;
89
+
87
90
#endregion
88
91
89
92
#region Constructors
90
93
91
94
/// <summary>
92
95
/// Creates a new instance of the Workspace class.
93
96
/// </summary>
94
- public WorkspaceService ( ILoggerFactory factory )
97
+ public WorkspaceService ( ILoggerFactory factory , IInternalPowerShellExecutionService executionService )
95
98
{
96
99
powerShellVersion = VersionUtils . PSVersion ;
97
100
logger = factory . CreateLogger < WorkspaceService > ( ) ;
98
101
WorkspaceFolders = new List < WorkspaceFolder > ( ) ;
99
102
ExcludeFilesGlob = new List < string > ( ) ;
100
103
FollowSymlinks = true ;
104
+ this . executionService = executionService ;
101
105
}
102
106
103
107
#endregion
@@ -139,19 +143,9 @@ public ScriptFile GetFile(DocumentUri documentUri)
139
143
// Make sure the file isn't already loaded into the workspace
140
144
if ( ! workspaceFiles . TryGetValue ( keyName , out ScriptFile scriptFile ) )
141
145
{
142
- // This method allows FileNotFoundException to bubble up
143
- // if the file isn't found.
144
- using ( StreamReader streamReader = OpenStreamReader ( documentUri ) )
145
- {
146
- scriptFile =
147
- new ScriptFile (
148
- documentUri ,
149
- streamReader ,
150
- powerShellVersion ) ;
151
-
152
- workspaceFiles [ keyName ] = scriptFile ;
153
- }
154
-
146
+ string fileContent = ReadFileContents ( documentUri ) ;
147
+ scriptFile = new ScriptFile ( documentUri , fileContent , powerShellVersion ) ;
148
+ workspaceFiles [ keyName ] = scriptFile ;
155
149
logger . LogDebug ( "Opened file on disk: " + documentUri . ToString ( ) ) ;
156
150
}
157
151
@@ -348,7 +342,6 @@ public IEnumerable<string> EnumeratePSFiles()
348
342
ignoreReparsePoints : ! FollowSymlinks
349
343
) ;
350
344
}
351
-
352
345
/// <summary>
353
346
/// Enumerate all the PowerShell (ps1, psm1, psd1) files in the workspace folders in a
354
347
/// recursive manner. Falls back to initial working directory if there are no workspace folders.
@@ -360,33 +353,22 @@ public IEnumerable<string> EnumeratePSFiles(
360
353
int maxDepth ,
361
354
bool ignoreReparsePoints )
362
355
{
363
- Matcher matcher = new ( ) ;
364
- foreach ( string pattern in includeGlobs ) { matcher . AddInclude ( pattern ) ; }
365
- foreach ( string pattern in excludeGlobs ) { matcher . AddExclude ( pattern ) ; }
356
+ PSCommand psCommand = new ( ) ;
357
+ psCommand . AddCommand ( @"Microsoft.PowerShell.Utility\Get-ChildItem" )
358
+ . AddParameter ( "Path" , WorkspacePaths )
359
+ . AddParameter ( "File" )
360
+ . AddParameter ( "Recurse" )
361
+ . AddParameter ( "ErrorAction" , "SilentlyContinue" )
362
+ . AddParameter ( "Force" )
363
+ . AddParameter ( "Include" , includeGlobs . Concat ( VersionUtils . IsNetCore ? s_psFileExtensionsCoreFramework : s_psFileExtensionsFullFramework ) )
364
+ . AddParameter ( "Exclude" , excludeGlobs )
365
+ . AddParameter ( "Depth" , maxDepth )
366
+ . AddParameter ( "FollowSymlink" , ! ignoreReparsePoints )
367
+ . AddCommand ( "Select-Object" )
368
+ . AddParameter ( "ExpandObject" , "FullName" ) ;
369
+ IEnumerable < string > results = executionService . ExecutePSCommandAsync < string > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) . GetAwaiter ( ) . GetResult ( ) ;
370
+ return results ;
366
371
367
- foreach ( string rootPath in WorkspacePaths )
368
- {
369
- if ( ! Directory . Exists ( rootPath ) )
370
- {
371
- continue ;
372
- }
373
-
374
- WorkspaceFileSystemWrapperFactory fsFactory = new (
375
- rootPath ,
376
- maxDepth ,
377
- VersionUtils . IsNetCore ? s_psFileExtensionsCoreFramework : s_psFileExtensionsFullFramework ,
378
- ignoreReparsePoints ,
379
- logger ) ;
380
-
381
- PatternMatchingResult fileMatchResult = matcher . Execute ( fsFactory . RootDirectory ) ;
382
- foreach ( FilePatternMatch item in fileMatchResult . Files )
383
- {
384
- // item.Path always contains forward slashes in paths when it should be backslashes on Windows.
385
- // Since we're returning strings here, it's important to use the correct directory separator.
386
- string path = VersionUtils . IsWindows ? item . Path . Replace ( '/' , Path . DirectorySeparatorChar ) : item . Path ;
387
- yield return Path . Combine ( rootPath , path ) ;
388
- }
389
- }
390
372
}
391
373
392
374
#endregion
@@ -403,10 +385,57 @@ internal static StreamReader OpenStreamReader(DocumentUri uri)
403
385
return new StreamReader ( fileStream , new UTF8Encoding ( ) , detectEncodingFromByteOrderMarks : true ) ;
404
386
}
405
387
406
- internal static string ReadFileContents ( DocumentUri uri )
388
+ internal string ReadFileContents ( DocumentUri uri )
407
389
{
408
- using StreamReader reader = OpenStreamReader ( uri ) ;
409
- return reader . ReadToEnd ( ) ;
390
+ PSCommand psCommand = new ( ) ;
391
+ string pspath ;
392
+ if ( uri . Scheme == Uri . UriSchemeFile )
393
+ {
394
+ pspath = uri . ToUri ( ) . LocalPath ;
395
+ }
396
+ else
397
+ {
398
+ string PSProvider = uri . Authority ;
399
+ string path = uri . Path ;
400
+ pspath = $ "{ PSProvider } ::{ path } ";
401
+ }
402
+ /* uri - "file:///c:/Users/dkattan/source/repos/immybot-ref/submodules/PowerShellEditorServices/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1"
403
+ * Authority = ""
404
+ * Fragment = ""
405
+ * Path = "/C:/Users/dkattan/source/repos/immybot-ref/submodules/PowerShellEditorServices/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1"
406
+ * Query = ""
407
+ * Scheme = "file"
408
+ * PSPath - "Microsoft.PowerShell.Core\FileSystem::C:\Users\dkattan\source\repos\immybot-ref\submodules\PowerShellEditorServices\test\PowerShellEditorServices.Test.Shared\Completion\CompletionExamples.psm1"
409
+ *
410
+ * Suggested Format:
411
+ * Authority = "Microsoft.PowerShell.Core\FileSystem"
412
+ * Scheme = "PSProvider"
413
+ * Path = "/C:/Users/dkattan/source/repos/immybot-ref/submodules/PowerShellEditorServices/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1"
414
+ * Result -> "PSProvider://Microsoft.PowerShell.Core/FileSystem::C:/Users/dkattan/source/repos/immybot-ref/submodules/PowerShellEditorServices/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1"
415
+ *
416
+ * Suggested Format 2:
417
+ * Authority = ""
418
+ * Scheme = "FileSystem"
419
+ * Path = "/C:/Users/dkattan/source/repos/immybot-ref/submodules/PowerShellEditorServices/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1"
420
+ * Result "FileSystem://c:/Users/dkattan/source/repos/immybot-ref/submodules/PowerShellEditorServices/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1"
421
+
422
+ */
423
+ psCommand . AddCommand ( "Get-Content" )
424
+ . AddParameter ( "LiteralPath" , pspath )
425
+ . AddParameter ( "Raw" , true )
426
+ . AddParameter ( "ErrorAction" , ActionPreference . Stop ) ;
427
+ try
428
+ {
429
+ IEnumerable < string > result = executionService . ExecutePSCommandAsync < string > ( psCommand , CancellationToken . None , new PowerShell . Execution . PowerShellExecutionOptions ( )
430
+ {
431
+ ThrowOnError = true
432
+ } ) . ConfigureAwait ( false ) . GetAwaiter ( ) . GetResult ( ) ;
433
+ return result . FirstOrDefault ( ) ;
434
+ }
435
+ catch ( ActionPreferenceStopException ex ) when ( ex . ErrorRecord . CategoryInfo . Category == ErrorCategory . ObjectNotFound && ex . ErrorRecord . TargetObject is string [ ] missingFiles && missingFiles . Count ( ) == 1 )
436
+ {
437
+ throw new FileNotFoundException ( ex . ErrorRecord . ToString ( ) , missingFiles . First ( ) , ex . ErrorRecord . Exception ) ;
438
+ }
410
439
}
411
440
412
441
internal string ResolveWorkspacePath ( string path ) => ResolveRelativeScriptPath ( InitialWorkingDirectory , path ) ;
@@ -429,10 +458,18 @@ internal string ResolveRelativeScriptPath(string baseFilePath, string relativePa
429
458
// Get the directory of the original script file, combine it
430
459
// with the given path and then resolve the absolute file path.
431
460
combinedPath =
432
- Path . GetFullPath (
433
- Path . Combine (
434
- baseFilePath ,
435
- relativePath ) ) ;
461
+ Path . GetFullPath (
462
+ Path . Combine (
463
+ baseFilePath ,
464
+ relativePath ) ) ;
465
+
466
+ PSCommand psCommand = new ( ) ;
467
+ psCommand . AddCommand ( "Resolve-Path" )
468
+ . AddParameter ( "Relative" , true )
469
+ . AddParameter ( "Path" , relativePath )
470
+ . AddParameter ( "RelativeBasePath" , baseFilePath ) ;
471
+ IEnumerable < string > result = executionService . ExecutePSCommandAsync < string > ( psCommand , CancellationToken . None ) . ConfigureAwait ( false ) . GetAwaiter ( ) . GetResult ( ) ;
472
+ combinedPath = result . FirstOrDefault ( ) ;
436
473
}
437
474
catch ( NotSupportedException e )
438
475
{
0 commit comments