I have some functions that write code as strings (for templating purposes) and I want these to be able to automatically keep indents correct.
Rather than having to tell each function call what indent depth this call is from, I'd prefer it to be able to automatically detect it.
I thought i might be able to use Get-PSCallStack but this doesn't seem to treat string interpolation as part of the stack. I see this if there is an exception as it doesn't tell me where in the string the exception interpolation happened. However when there is an exception, I can use gerr -Newest 2
and this does contain the actual line number of the string in the template to give me a chance of debugging. I'm not sure how to reproduce this "newest 2" behaviour at runtime however
So this works
function Get-PosInString { param($SomeParam) # debug to see where it thinks we are in the stack trace (Get-PSCallStack)[1] | format-list | Out-String (Get-PSCallStack)[1].position | format-list | Out-String # actual output of this function Write-Output (((Get-PSCallStack)[1]).Position.StartOffset - 2) # -2 as there is always a $( at the beginning}"hello $(Get-PosInString)"
ultimately returns the correct value "7"
ScriptName :ScriptLineNumber : 1InvocationInfo : System.Management.Automation.InvocationInfoPosition : Get-PosInStringFunctionName : <ScriptBlock>Command : <ScriptBlock>Location : <No file>Arguments : {}File :StartScriptPosition : System.Management.Automation.Language.InternalScriptPositionEndScriptPosition : System.Management.Automation.Language.InternalScriptPositionStartLineNumber : 1StartColumnNumber : 10EndLineNumber : 1EndColumnNumber : 25Text : Get-PosInStringStartOffset : 9EndOffset : 24 7
however if I then do some additional interpolation on one of the input args to the function (which I also need to be able to do)
"hello $(Get-PosInString "$('argvalue')")"
I get result
helloScriptName :ScriptLineNumber : 1InvocationInfo : System.Management.Automation.InvocationInfoPosition : 'argvalue'FunctionName : <ScriptBlock>Command : <ScriptBlock>Location : <No file>Arguments : {}File :StartScriptPosition : System.Management.Automation.Language.InternalScriptPositionEndScriptPosition : System.Management.Automation.Language.InternalScriptPositionStartLineNumber : 1StartColumnNumber : 41EndLineNumber : 1EndColumnNumber : 46Text : 'argvalue'StartOffset : 40EndOffset : 45 38
which is now taking the last stack item to be the parameter I passed in and it doesn't seem possible to get to the stack of the item that actually called the function.
This feels to me like a powershell bug. I would expect that the interpolation of the caller argument should have been dealt with during the call, and with the execution point now past that and inside the function, the stack should be consistent regardless of any parameter interpolation on the call.