33// Licensed under the MIT license. See LICENSE file in the project root for full license information.
44//
55
6+ using System ;
67using System . Collections . Generic ;
78using System . Management . Automation . Language ;
9+ using Microsoft . PowerShell . EditorServices . Utility ;
810
911namespace Microsoft . PowerShell . EditorServices
1012{
@@ -13,14 +15,21 @@ namespace Microsoft.PowerShell.EditorServices
1315 /// </summary>
1416 internal class FindDotSourcedVisitor : AstVisitor
1517 {
16- /// <summary>
17- /// A hash set of the dot sourced files (because we don't want duplicates)
18- /// </summary>
18+ private readonly string _psScriptRoot ;
19+
20+ /// <summary>
21+ /// A hash set of the dot sourced files (because we don't want duplicates)
22+ /// </summary>
1923 public HashSet < string > DotSourcedFiles { get ; private set ; }
2024
21- public FindDotSourcedVisitor ( )
25+ /// <summary>
26+ /// Creates a new instance of the FindDotSourcedVisitor class.
27+ /// </summary>
28+ /// <param name="psScriptRoot">Pre-calculated value of $PSScriptRoot</param>
29+ public FindDotSourcedVisitor ( string psScriptRoot )
2230 {
23- this . DotSourcedFiles = new HashSet < string > ( ) ;
31+ DotSourcedFiles = new HashSet < string > ( StringComparer . CurrentCultureIgnoreCase ) ;
32+ _psScriptRoot = psScriptRoot ;
2433 }
2534
2635 /// <summary>
@@ -32,15 +41,50 @@ public FindDotSourcedVisitor()
3241 /// or a decision to continue if it wasn't found</returns>
3342 public override AstVisitAction VisitCommand ( CommandAst commandAst )
3443 {
35- if ( commandAst . InvocationOperator . Equals ( TokenKind . Dot ) &&
36- commandAst . CommandElements [ 0 ] is StringConstantExpressionAst )
44+ CommandElementAst commandElementAst = commandAst . CommandElements [ 0 ] ;
45+ if ( commandAst . InvocationOperator . Equals ( TokenKind . Dot ) )
3746 {
38- // Strip any quote characters off of the string
39- string fileName = commandAst . CommandElements [ 0 ] . Extent . Text . Trim ( '\' ' , '"' ) ;
40- DotSourcedFiles . Add ( fileName ) ;
47+ string path ;
48+ switch ( commandElementAst )
49+ {
50+ case StringConstantExpressionAst stringConstantExpressionAst :
51+ path = stringConstantExpressionAst . Value ;
52+ break ;
53+
54+ case ExpandableStringExpressionAst expandableStringExpressionAst :
55+ path = GetPathFromExpandableStringExpression ( expandableStringExpressionAst ) ;
56+ break ;
57+
58+ default :
59+ path = null ;
60+ break ;
61+ }
62+
63+ if ( ! string . IsNullOrWhiteSpace ( path ) )
64+ {
65+ DotSourcedFiles . Add ( PathUtils . NormalizePathSeparators ( path ) ) ;
66+ }
4167 }
4268
4369 return base . VisitCommand ( commandAst ) ;
4470 }
71+
72+ private string GetPathFromExpandableStringExpression ( ExpandableStringExpressionAst expandableStringExpressionAst )
73+ {
74+ var path = expandableStringExpressionAst . Value ;
75+ foreach ( var nestedExpression in expandableStringExpressionAst . NestedExpressions )
76+ {
77+ // If the string contains the variable $PSScriptRoot, we replace it with the corresponding value.
78+ if ( ! ( nestedExpression is VariableExpressionAst variableAst
79+ && variableAst . VariablePath . UserPath . Equals ( "PSScriptRoot" , StringComparison . OrdinalIgnoreCase ) ) )
80+ {
81+ return null ; // We return null instead of a partially evaluated ExpandableStringExpression.
82+ }
83+
84+ path = path . Replace ( variableAst . ToString ( ) , _psScriptRoot ) ;
85+ }
86+
87+ return path ;
88+ }
4589 }
4690}
0 commit comments