66using System . Collections . Generic ;
77using System . IO ;
88using System . Linq ;
9+ using System . Management . Automation ;
910using System . Security ;
1011using System . Text ;
11- using Microsoft . Extensions . FileSystemGlobbing ;
12+ using System . Threading ;
1213using Microsoft . Extensions . Logging ;
14+ using Microsoft . PowerShell . EditorServices . Services . PowerShell ;
1315using Microsoft . PowerShell . EditorServices . Services . TextDocument ;
14- using Microsoft . PowerShell . EditorServices . Services . Workspace ;
1516using Microsoft . PowerShell . EditorServices . Utility ;
1617using OmniSharp . Extensions . LanguageServer . Protocol ;
1718using OmniSharp . Extensions . LanguageServer . Protocol . Models ;
@@ -84,20 +85,23 @@ internal class WorkspaceService
8485 /// </summary>
8586 public bool FollowSymlinks { get ; set ; }
8687
88+ private readonly IInternalPowerShellExecutionService executionService ;
89+
8790 #endregion
8891
8992 #region Constructors
9093
9194 /// <summary>
9295 /// Creates a new instance of the Workspace class.
9396 /// </summary>
94- public WorkspaceService ( ILoggerFactory factory )
97+ public WorkspaceService ( ILoggerFactory factory , IInternalPowerShellExecutionService executionService )
9598 {
9699 powerShellVersion = VersionUtils . PSVersion ;
97100 logger = factory . CreateLogger < WorkspaceService > ( ) ;
98101 WorkspaceFolders = new List < WorkspaceFolder > ( ) ;
99102 ExcludeFilesGlob = new List < string > ( ) ;
100103 FollowSymlinks = true ;
104+ this . executionService = executionService ;
101105 }
102106
103107 #endregion
@@ -139,19 +143,9 @@ public ScriptFile GetFile(DocumentUri documentUri)
139143 // Make sure the file isn't already loaded into the workspace
140144 if ( ! workspaceFiles . TryGetValue ( keyName , out ScriptFile scriptFile ) )
141145 {
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 ;
155149 logger . LogDebug ( "Opened file on disk: " + documentUri . ToString ( ) ) ;
156150 }
157151
@@ -348,7 +342,6 @@ public IEnumerable<string> EnumeratePSFiles()
348342 ignoreReparsePoints : ! FollowSymlinks
349343 ) ;
350344 }
351-
352345 /// <summary>
353346 /// Enumerate all the PowerShell (ps1, psm1, psd1) files in the workspace folders in a
354347 /// recursive manner. Falls back to initial working directory if there are no workspace folders.
@@ -360,33 +353,22 @@ public IEnumerable<string> EnumeratePSFiles(
360353 int maxDepth ,
361354 bool ignoreReparsePoints )
362355 {
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 ;
366371
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- }
390372 }
391373
392374 #endregion
@@ -403,10 +385,57 @@ internal static StreamReader OpenStreamReader(DocumentUri uri)
403385 return new StreamReader ( fileStream , new UTF8Encoding ( ) , detectEncodingFromByteOrderMarks : true ) ;
404386 }
405387
406- internal static string ReadFileContents ( DocumentUri uri )
388+ internal string ReadFileContents ( DocumentUri uri )
407389 {
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+ }
410439 }
411440
412441 internal string ResolveWorkspacePath ( string path ) => ResolveRelativeScriptPath ( InitialWorkingDirectory , path ) ;
@@ -429,10 +458,18 @@ internal string ResolveRelativeScriptPath(string baseFilePath, string relativePa
429458 // Get the directory of the original script file, combine it
430459 // with the given path and then resolve the absolute file path.
431460 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 ( ) ;
436473 }
437474 catch ( NotSupportedException e )
438475 {
0 commit comments