12
12
using System . Threading . Tasks ;
13
13
using Microsoft . PowerShell . EditorServices . Debugging ;
14
14
using Microsoft . PowerShell . EditorServices . Utility ;
15
- using System . IO ;
16
15
using Microsoft . PowerShell . EditorServices . Session ;
17
16
18
17
namespace Microsoft . PowerShell . EditorServices
@@ -26,6 +25,7 @@ public class DebugService
26
25
#region Fields
27
26
28
27
private const string PsesGlobalVariableNamePrefix = "__psEditorServices_" ;
28
+ private const string TemporaryScriptFileName = "TemporaryScript.ps1" ;
29
29
30
30
private PowerShellContext powerShellContext ;
31
31
private RemoteFileManager remoteFileManager ;
@@ -35,6 +35,7 @@ public class DebugService
35
35
new Dictionary < string , List < Breakpoint > > ( ) ;
36
36
37
37
private int nextVariableId ;
38
+ private string temporaryScriptListingPath ;
38
39
private List < VariableDetailsBase > variables ;
39
40
private VariableContainerDetails globalScopeVariables ;
40
41
private VariableContainerDetails scriptScopeVariables ;
@@ -93,8 +94,8 @@ public DebugService(
93
94
/// <param name="clearExisting">If true, causes all existing breakpoints to be cleared before setting new ones.</param>
94
95
/// <returns>An awaitable Task that will provide details about the breakpoints that were set.</returns>
95
96
public async Task < BreakpointDetails [ ] > SetLineBreakpoints (
96
- ScriptFile scriptFile ,
97
- BreakpointDetails [ ] breakpoints ,
97
+ ScriptFile scriptFile ,
98
+ BreakpointDetails [ ] breakpoints ,
98
99
bool clearExisting = true )
99
100
{
100
101
var resultBreakpointDetails = new List < BreakpointDetails > ( ) ;
@@ -111,22 +112,32 @@ public async Task<BreakpointDetails[]> SetLineBreakpoints(
111
112
if ( this . powerShellContext . CurrentRunspace . Location == RunspaceLocation . Remote &&
112
113
this . remoteFileManager != null )
113
114
{
114
- string mappedPath =
115
- this . remoteFileManager . GetMappedPath (
116
- scriptPath ,
117
- this . powerShellContext . CurrentRunspace ) ;
118
-
119
- if ( mappedPath == null )
115
+ if ( ! this . remoteFileManager . IsUnderRemoteTempPath ( scriptPath ) )
120
116
{
121
117
Logger . Write (
122
- LogLevel . Error ,
123
- $ "Could not map local path '{ scriptPath } ' to a remote path .") ;
118
+ LogLevel . Verbose ,
119
+ $ "Could not set breakpoints for local path '{ scriptPath } ' in a remote session .") ;
124
120
125
121
return resultBreakpointDetails . ToArray ( ) ;
126
122
}
127
123
124
+ string mappedPath =
125
+ this . remoteFileManager . GetMappedPath (
126
+ scriptPath ,
127
+ this . powerShellContext . CurrentRunspace ) ;
128
+
128
129
scriptPath = mappedPath ;
129
130
}
131
+ else if (
132
+ this . temporaryScriptListingPath != null &&
133
+ this . temporaryScriptListingPath . Equals ( scriptPath , StringComparison . CurrentCultureIgnoreCase ) )
134
+ {
135
+ Logger . Write (
136
+ LogLevel . Verbose ,
137
+ $ "Could not set breakpoint on temporary script listing path '{ scriptPath } '.") ;
138
+
139
+ return resultBreakpointDetails . ToArray ( ) ;
140
+ }
130
141
131
142
// Fix for issue #123 - file paths that contain wildcard chars [ and ] need to
132
143
// quoted and have those wildcard chars escaped.
@@ -622,7 +633,7 @@ private async Task ClearCommandBreakpoints()
622
633
await this . powerShellContext . ExecuteCommand < object > ( psCommand ) ;
623
634
}
624
635
625
- private async Task FetchStackFramesAndVariables ( )
636
+ private async Task FetchStackFramesAndVariables ( string scriptNameOverride )
626
637
{
627
638
this . nextVariableId = VariableDetailsBase . FirstVariableId ;
628
639
this . variables = new List < VariableDetailsBase > ( ) ;
@@ -633,7 +644,7 @@ private async Task FetchStackFramesAndVariables()
633
644
// Must retrieve global/script variales before stack frame variables
634
645
// as we check stack frame variables against globals.
635
646
await FetchGlobalAndScriptVariables ( ) ;
636
- await FetchStackFrames ( ) ;
647
+ await FetchStackFrames ( scriptNameOverride ) ;
637
648
}
638
649
639
650
private async Task FetchGlobalAndScriptVariables ( )
@@ -750,7 +761,7 @@ private bool AddToAutoVariables(PSObject psvariable, string scope)
750
761
return true ;
751
762
}
752
763
753
- private async Task FetchStackFrames ( )
764
+ private async Task FetchStackFrames ( string scriptNameOverride )
754
765
{
755
766
PSCommand psCommand = new PSCommand ( ) ;
756
767
@@ -782,7 +793,12 @@ private async Task FetchStackFrames()
782
793
StackFrameDetails . Create ( callStackFrames [ i ] , autoVariables , localVariables ) ;
783
794
784
795
string stackFrameScriptPath = this . stackFrameDetails [ i ] . ScriptPath ;
785
- if ( this . powerShellContext . CurrentRunspace . Location == RunspaceLocation . Remote &&
796
+ if ( scriptNameOverride != null &&
797
+ string . Equals ( stackFrameScriptPath , StackFrameDetails . NoFileScriptPath ) )
798
+ {
799
+ this . stackFrameDetails [ i ] . ScriptPath = scriptNameOverride ;
800
+ }
801
+ else if ( this . powerShellContext . CurrentRunspace . Location == RunspaceLocation . Remote &&
786
802
this . remoteFileManager != null &&
787
803
! string . Equals ( stackFrameScriptPath , StackFrameDetails . NoFileScriptPath ) )
788
804
{
@@ -979,6 +995,25 @@ private string FormatInvalidBreakpointConditionMessage(string condition, string
979
995
return $ "'{ condition } ' is not a valid PowerShell expression. { message } ";
980
996
}
981
997
998
+ private string TrimScriptListingLine ( PSObject scriptLineObj , ref int prefixLength )
999
+ {
1000
+ string scriptLine = scriptLineObj . ToString ( ) ;
1001
+
1002
+ if ( ! string . IsNullOrWhiteSpace ( scriptLine ) )
1003
+ {
1004
+ if ( prefixLength == 0 )
1005
+ {
1006
+ // The prefix is a padded integer ending with ':', an asterisk '*'
1007
+ // if this is the current line, and one character of padding
1008
+ prefixLength = scriptLine . IndexOf ( ':' ) + 2 ;
1009
+ }
1010
+
1011
+ return scriptLine . Substring ( prefixLength ) ;
1012
+ }
1013
+
1014
+ return null ;
1015
+ }
1016
+
982
1017
#endregion
983
1018
984
1019
#region Events
@@ -990,11 +1025,56 @@ private string FormatInvalidBreakpointConditionMessage(string condition, string
990
1025
991
1026
private async void OnDebuggerStop ( object sender , DebuggerStopEventArgs e )
992
1027
{
1028
+ bool noScriptName = false ;
1029
+ string localScriptPath = e . InvocationInfo . ScriptName ;
1030
+
1031
+ // If there's no ScriptName, get the "list" of the current source
1032
+ if ( this . remoteFileManager != null && string . IsNullOrEmpty ( localScriptPath ) )
1033
+ {
1034
+ // Get the current script listing and create the buffer
1035
+ PSCommand command = new PSCommand ( ) ;
1036
+ command . AddScript ( $ "list 1 { int . MaxValue } ") ;
1037
+
1038
+ IEnumerable < PSObject > scriptListingLines =
1039
+ await this . powerShellContext . ExecuteCommand < PSObject > (
1040
+ command , false , false ) ;
1041
+
1042
+ if ( scriptListingLines != null )
1043
+ {
1044
+ int linePrefixLength = 0 ;
1045
+
1046
+ string scriptListing =
1047
+ string . Join (
1048
+ Environment . NewLine ,
1049
+ scriptListingLines
1050
+ . Select ( o => this . TrimScriptListingLine ( o , ref linePrefixLength ) )
1051
+ . Where ( s => s != null ) ) ;
1052
+
1053
+ this . temporaryScriptListingPath =
1054
+ this . remoteFileManager . CreateTemporaryFile (
1055
+ TemporaryScriptFileName ,
1056
+ scriptListing ,
1057
+ this . powerShellContext . CurrentRunspace ) ;
1058
+
1059
+ localScriptPath =
1060
+ this . temporaryScriptListingPath
1061
+ ?? StackFrameDetails . NoFileScriptPath ;
1062
+
1063
+ noScriptName = localScriptPath != null ;
1064
+ }
1065
+ else
1066
+ {
1067
+ Logger . Write (
1068
+ LogLevel . Warning ,
1069
+ $ "Could not load script context") ;
1070
+ }
1071
+ }
1072
+
993
1073
// Get call stack and variables.
994
- await this . FetchStackFramesAndVariables ( ) ;
1074
+ await this . FetchStackFramesAndVariables (
1075
+ noScriptName ? localScriptPath : null ) ;
995
1076
996
1077
// If this is a remote connection, get the file content
997
- string localScriptPath = e . InvocationInfo . ScriptName ;
998
1078
if ( this . powerShellContext . CurrentRunspace . Location == RunspaceLocation . Remote &&
999
1079
this . remoteFileManager != null )
1000
1080
{
0 commit comments