Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/System.Management.Automation/engine/MshCmdlet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ public CommandInfo GetCommand(string commandName, CommandTypes type, object[] ar
/// </summary>
public System.EventHandler<CommandLookupEventArgs> PostCommandLookupAction { get; set; }

/// <summary>
/// Gets or sets the action that is invoked everytime the runspace location (cwd) is changed.
/// </summary>
public System.EventHandler<LocationChangedEventArgs> LocationChangedAction { get; set; }

/// <summary>
/// Returns the CmdletInfo object that corresponds to the name argument
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context)
throw PSTraceSource.NewArgumentNullException("path");
}

PathInfo current = CurrentLocation;
string originalPath = path;
string driveName = null;
ProviderInfo provider = null;
Expand Down Expand Up @@ -553,6 +554,15 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context)
// Set the $PWD variable to the new location

this.SetVariable(SpecialVariables.PWDVarPath, this.CurrentLocation, false, true, CommandOrigin.Internal);

// If an action has been defined for location changes, invoke it now.
if (PublicSessionState.InvokeCommand.LocationChangedAction != null)
{
var eventArgs = new LocationChangedEventArgs(PublicSessionState, current, CurrentLocation);
PublicSessionState.InvokeCommand.LocationChangedAction.Invoke(ExecutionContext.CurrentRunspace, eventArgs);
s_tracer.WriteLine("Invoked LocationChangedAction");
}

return this.CurrentLocation;
} // SetLocation

Expand Down Expand Up @@ -1046,5 +1056,47 @@ internal PathInfoStack SetDefaultLocationStack(string stackName)

#endregion push-Pop current working directory
} // SessionStateInternal class

/// <summary>
/// Event argument for the LocationChangedAction containing
/// information about the old location we were in and the new
/// location we changed to.
/// </summary>
public class LocationChangedEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the LocationChangedEventArgs class.
/// </summary>
/// <param name="sessionState">
/// The public session state instance associated with this runspace.
/// </param>
/// <param name="oldPath">
/// The path we changed locations from.
/// </param>
/// <param name="newPath">
/// The path we change locations to.
/// </param>
internal LocationChangedEventArgs(SessionState sessionState, PathInfo oldPath, PathInfo newPath)
{
SessionState = sessionState;
OldPath = oldPath;
NewPath = newPath;
}

/// <summary>
/// Gets the path we changed location from.
/// </summary>
public PathInfo OldPath { get; internal set; }

/// <summary>
/// Gets the path we changed location to.
/// </summary>
public PathInfo NewPath { get; internal set; }

/// <summary>
/// Gets the session state instance for the current runspace.
/// </summary>
public SessionState SessionState { get; internal set; }
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,40 @@ Describe "Set-Location" -Tags "CI" {
{ Set-Location - } | Should -Throw -ErrorId 'System.InvalidOperationException,Microsoft.PowerShell.Commands.SetLocationCommand'
}
}

Context 'Test the LocationChangedAction event handler' {

AfterEach {
$ExecutionContext.InvokeCommand.LocationChangedAction = $null
}

It 'The LocationChangedAction should fire when changing location' {
$initialPath = $pwd
$oldPath = $null
$newPath = $null
$eventSessionState = $null
$eventRunspace = $null
$ExecutionContext.InvokeCommand.LocationChangedAction = {
(Get-Variable eventRunspace).Value = $this
(Get-Variable eventSessionState).Value = $_.SessionState
(Get-Variable oldPath).Value = $_.oldPath
(Get-Variable newPath).Value = $_.newPath
}
Set-Location ..
$newPath.Path | Should -Be $pwd.Path
$oldPath.Path | Should -Be $initialPath.Path
$eventSessionState | Should -Be $ExecutionContext.SessionState
$eventRunspace | Should -Be ([runspace]::DefaultRunspace)
}

It 'Errors in the LocationChangedAction should be catchable but not fail the cd' {
$location = $PWD
Set-Location ..
$ExecutionContext.InvokeCommand.LocationChangedAction = { throw "Boom" }
# Verify that the exception occurred
{ Set-Location $location } | Should Throw "Boom"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this should be Should -Throw -ErrorId "Boom" here

# But the location should still have changed
$PWD.Path | Should -Be $location.Path
}
}
}